Files
stalwart-migrator/docs/docker.md
T
jcoffey-dev 6608f3a97f Shorten the README; move the technical detail into docs/
The README keeps what the tool is, how to install it and the first commands,
and points to the guide on docs.ihasmail.org. Everything else moves, whole,
into docs/ and CONTRIBUTING.md, where it is organized for readers who want
the detail. Where the old README disagreed with the code, the code wins.
2026-09-15 12:14:36 -07:00

87 lines
4.0 KiB
Markdown

# Docker deployments
How `run` migrates a Stalwart running in a Docker container: the flags it
needs, what it carries across, what it refuses, and what it keeps for a manual
restore. For the short version, see the [README](../README.md).
## The container path is unproven
**The container path has never completed a migration against a real Stalwart
image.** What it inspects and what it assembles have been checked against one,
which is how two problems were found and fixed (#11) — but a fake `docker`
still proves only that the right commands are assembled, not that the image
reads the config it is handed and comes up as the server it was.
`run` refuses a container deployment unless you pass
`--container-path-unproven`, which is there so nobody reaches it without being
told. Rehearse on a clone first ([rehearsal.md](rehearsal.md)); that advice
goes double here.
## Flags
- `--target-image` names the image in full, e.g.
`stalwartlabs/stalwart:v0.16.14`. It is never derived from the running
container by swapping the tag — that is wrong for a digest-pinned image, a
mirror or a fork, and being wrong means pulling the wrong software into a
mail server.
- `--container` names the container (default `stalwart`).
- `--data-dir` must name the path **inside** the container, since that is
where its data actually lives. `preflight` says so if it matches none of the
container's mounts.
## What cutover carries across, and what it refuses
A container cannot be edited in place the way a unit file can, so cutting one
over means rebuilding it. A container rebuilt without its capabilities, its
custom network or its device mappings starts cleanly and is quietly not the
server it was.
So cutover carries across what it understands and refuses outright when it
finds anything else, naming what it found. It asks that question in
`preflight`, while the server is still running, rather than only at cutover
after it has stopped.
**It carries:** mounts, ports, environment, restart policy, labels, and
anything the container overrides on its image — a `--user`, an
`--entrypoint`, a command of your own. What the container merely *inherits*
from its old image is left to the new one, whose own defaults are the ones
that go with it.
**It refuses:**
- a container with settings it doesn't understand, such as extra
capabilities, a custom network or device mappings;
- a container whose data is not on a volume — an upgrade replaces the
container, and the writable layer goes with it;
- a container managed by Docker Compose, because recreating it out from under
compose leaves the container and the compose file disagreeing about what is
deployed, and the next `compose up` reverts the migration. Compose
deployments are migrated by editing the image tag in the compose file and
running `compose up -d`.
## Where the converted config goes
The converted v0.16 config is written into the host side of whichever mount
covers `--data-dir`, named on the container side, and the recreated container
is started with `--config` pointing at it.
It cannot go anywhere else: cutover recreates a container with the mounts it
had and cannot invent a new one. The official image's own default is
`--config /etc/stalwart/config.json`, which is a *different* volume, so a
container left to that default would come up on whatever the old version had
left there. If your container overrides its command, cutover refuses rather
than merging the two — both are the container's argv and there is no honest
way to guess.
## What it keeps
The old container is renamed rather than removed, the old image is never
pruned, and the container's `docker inspect` is preserved as an artifact before
anything is replaced. Together those are the manual restore path — see
[recovery.md](recovery.md), which applies here exactly as it does to a binary
install.
Measured on a full migration: the store converts in seconds, and the service
was down for **6 seconds** end to end. Plan the window around verification,
not data volume.