mabel.ink

Reversible cutovers, or how to containerize a service you are still using

July 20, 2026

Moving a service into a container is easy when nobody is using it. Moving a media server that the household streams from, or the DNS resolver every device on the network depends on, is a different exercise. The container part is trivial. Not breaking anything is the work.

I have done this twice now on my home server, and the pattern that made both safe is the same five things.

1. Capture the current state before you touch it

You cannot verify a migration you never measured. Before changing anything I wrote down the version, the service user and its group memberships, the data directory, every path the application had stored internally, and the settings that were not at their defaults.

That last one matters more than it sounds. A service you have been running for a year has accumulated configuration you no longer remember making. If the container comes up with stock defaults and something feels subtly wrong three days later, you will have no baseline to compare against.

I scripted the capture so it could be re-run, and had it write a summary with secrets redacted alongside a full dump that stays untracked. The summary is the thing you actually read. The full dump is for when the summary was not enough.

2. Copy the data. Never move it.

This is the whole trick.

The cutover script copies the native data directory into the container's config volume. It does not move it, symlink it, or point the container at the live directory. The original stays exactly where it was, untouched, still owned by the still-installed native package.

That costs disk space and buys an instant rollback. The old service is not degraded or half-dismantled. It is sitting there, complete, one systemctl start from being live again.

If you move the data instead, rollback means restoring from backup, and you will find out whether your backups work at the worst possible moment.

3. Keep the endpoint identical

Both migrations used host networking so the containerized service answers on the same address and port as the native one did.

That is not the elegant container-native choice. Bridge networking with published ports is tidier. But every client on the network already points at that host and port, and half of them are televisions and phones I do not want to reconfigure. Host networking also keeps client IPs intact for anything that logs or filters by source address, which matters for a DNS server.

The migration should be invisible from outside the box. If clients need reconfiguring, it is not a cutover, it is a redeployment with an outage attached.

4. Mount data at exactly the same path inside the container

This is the one that would have bitten me.

The media server stores its library locations as absolute paths in its own database. Not relative, not as identifiers. Literal paths like /mnt/media/Movies.

Mount that share at /media inside the container, which is what the image's documentation suggests, and the service starts fine, the container looks healthy, and every library is empty because the paths it has stored no longer exist. You would then be tempted to "fix" it by re-pointing the libraries, which loses watch history and metadata associations.

So the compose file mounts the share at the identical path the host uses. The container's filesystem layout is shaped by what the application already believes, not by what is conventional.

The general form: applications remember absolute paths. If you are moving an app's execution context, the paths it stored must still resolve, or you are not migrating it, you are reinstalling it.

5. Write the rollback before you run the cutover

Not "have a rollback plan." Write the actual commands down, in the README, before you start.

cd /opt/containers/<service> && docker compose down
sudo systemctl enable --now <service>

Two lines, and they only work because of step 2. But writing them out beforehand forces you to check that they are true. If you cannot write the rollback in advance, you have not finished designing the migration.

Doing it at three in the morning, from memory, with the network down, is not the moment to discover that your rollback needed a step you had not thought of.

The pattern underneath

Reversibility is not a fallback you improvise. It is a property you build in, and it costs something up front: disk for the duplicate data, a less idiomatic network mode, a container filesystem shaped by legacy paths.

What you get is the ability to attempt the migration on a normal Tuesday instead of scheduling a maintenance window and hoping. The cheaper it is to undo, the less it matters whether you got it right the first time.

Which, when the alternative is the household losing DNS or the TV losing its watch history, is the only reason I was willing to try it at all.