← Back home

2026.07 / MAIL RECOVERY 031

Back Up and Restore Docker Mailserver: A Tested Recovery Runbook

A tar file called mail-backup.tar.gz is not yet a backup strategy. For Docker Mailserver, recovery depends on knowing which mounts hold messages and state, preserving the configuration that explains those files, keeping the copy away from the VPS, and proving that another container can read it.

This runbook is for a conventional Docker Compose installation with bind mounts. The paths are generic on purpose: compare them with your own Compose file before running anything. If you use named volumes, the inventory step changes, but the recovery principles do not.

The useful test is not “did tar exit zero?” It is “can a clean, isolated Docker Mailserver instance start and expose a known mailbox from this archive?”

What must be recoverable?

A typical Docker Mailserver service persists four areas:

The official Docker Mailserver backup FAQ demonstrates archiving those mounts with a temporary container. Also preserve your Compose file, non-secret environment template, exact image version, and any external certificate arrangement. Treat the real environment file, account hashes and DKIM private keys as secrets.

1. Inventory the real mounts before copying

Do not assume that a directory named mail-data is the mounted directory. Ask Docker what the service currently uses:

docker compose config

docker inspect mailserver \
  --format '{{range .Mounts}}{{println .Source " -> " .Destination}}{{end}}'

docker image inspect mailserver/docker-mailserver:latest \
  --format '{{index .RepoDigests 0}}'

Use the actual service or container name and record the immutable image digest. Avoid putting an unredacted docker compose config dump in a ticket or public repository because interpolated secrets can appear in its output.

For the examples below, assume this project layout:

mail-stack/
├── compose.yaml
├── mailserver.env
└── docker-data/dms/
    ├── config/
    ├── mail-data/
    ├── mail-state/
    └── mail-logs/

2. Choose a consistency level deliberately

A live archive can be useful for frequent snapshots, and the DMS FAQ shows that pattern. However, files can change while tar walks the tree. For a simple server, a short controlled shutdown gives the clearest recovery point:

cd /srv/mail-stack

docker compose down

tar --xattrs --acls --numeric-owner -czf \
  ../mail-backups/dms-$(date -u +%Y%m%dT%H%M%SZ).tar.gz \
  compose.yaml mailserver.env docker-data/dms/

docker compose up -d

Use docker compose down, not down -v; the latter can remove named volumes. Docker Mailserver’s usage documentation recommends Compose up/down rather than relying on container start/stop semantics.

If inbound downtime is unacceptable, make frequent filesystem snapshots or live archives and schedule a less frequent quiesced recovery point. A remote sender normally queues and retries after a temporary failure, but maintenance still deserves monitoring and a defined window.

3. Validate and protect the archive

Run checks before moving or rotating the file:

BACKUP=../mail-backups/dms-20260717T100000Z.tar.gz

tar -tzf "$BACKUP" >/dev/null
sha256sum "$BACKUP" > "$BACKUP.sha256"
chmod 600 "$BACKUP" "$BACKUP.sha256"

The checksum detects damage; it does not provide confidentiality or prove who created the archive. Mail backups contain private correspondence, password hashes and signing keys. Encrypt the archive with a tool whose recovery key is stored separately, then copy it to storage outside the mail host. Do not publish the archive name, remote destination, key location or encryption secret.

A practical retention policy might keep several daily, weekly and monthly recovery points, but capacity is not the only consideration. Retention should also match users’ privacy expectations and any legal obligations. At least one copy should survive loss of the VPS account or disk.

4. Restore into an empty staging directory

Never test by extracting over production. On an isolated recovery host with a compatible Docker installation:

mkdir -p /srv/dms-restore-test
cd /srv/dms-restore-test

sha256sum --check dms-backup.tar.gz.sha256

tar --xattrs --acls --numeric-owner -xzf dms-backup.tar.gz

find docker-data/dms -maxdepth 2 -type d -print

Inspect paths before starting anything. A common restore mistake creates an extra directory level such as docker-data/dms/docker-data/dms/, leaving the container attached to an apparently empty mailbox.

Restore with the same DMS image version or digest first. An incident is a poor time to combine data recovery with a major upgrade. Once the recovered service works, upgrade it as a separate, reversible change.

5. Keep the recovery test off the public mail network

A clone with production accounts and DKIM keys must not accidentally receive or send real mail. Use an isolated Docker network, do not publish SMTP ports, and disable outbound network access where practical. At minimum, do not change public MX records and do not attach the test to a public reverse proxy.

Create a recovery-only Compose override rather than editing the archived production file:

# compose.restore.yaml
services:
  mailserver:
    image: mailserver/docker-mailserver:<recorded-version>
    ports: []
    networks:
      - recovery

networks:
  recovery:
    internal: true

Then render the merged configuration and check that every source path points inside the staging tree:

docker compose -f compose.yaml -f compose.restore.yaml config

docker compose -f compose.yaml -f compose.restore.yaml up -d

Some DMS features need DNS or package-independent network access during startup, so a fully internal network may require a recovery-specific adjustment. If you allow egress temporarily, continue to leave all inbound ports unpublished and prevent outbound SMTP at the host firewall.

6. Prove mailbox and configuration recovery

A green container is only the first check:

docker compose -f compose.yaml -f compose.restore.yaml ps

docker compose -f compose.yaml -f compose.restore.yaml logs --tail 200

docker compose -f compose.yaml -f compose.restore.yaml \
  exec mailserver setup email list

docker compose -f compose.yaml -f compose.restore.yaml \
  exec mailserver doveadm mailbox list -u [email protected]

docker compose -f compose.yaml -f compose.restore.yaml \
  exec mailserver doveadm force-resync -u [email protected] INBOX

Use a designated test mailbox and replace the example address. Confirm that expected folders exist, a known old message can be found through IMAP or Dovecot tooling, aliases are present, and account authentication works locally. Do not paste message bodies, account lists, logs or hashes into public build output.

If mail exists on disk but folders look wrong, stop and investigate mount paths, numeric ownership and extraction options before recursively changing permissions. A broad chown can make a mismatched layout harder to diagnose.

7. Document the production cutover, but do not improvise it

A real replacement-host recovery normally follows this order:

  1. Provision and harden the replacement host.
  2. Install a compatible Docker and Compose version.
  3. Verify the encrypted archive and restore it while DMS is stopped.
  4. Start the recorded DMS image and run local mailbox checks.
  5. Recreate required TLS, firewall and DNS conditions without exposing admin services.
  6. Change public mail routing only after SMTP, IMAP, authentication and TLS tests pass.
  7. Watch logs and queues, then keep the old host unavailable but intact during the rollback window.

DNS is not part of the tar archive. Record the intended MX, SPF, DKIM selector, DMARC, mail-host address and PTR relationships in a redacted runbook. PTR is controlled at the server provider and may need separate work after a host move.

Failure modes worth testing

A compact quarterly recovery drill

  1. Inventory mounts and record the running image digest.
  2. Create a controlled archive and restart production.
  3. Verify tar readability and checksum.
  4. Encrypt and transfer a copy off-host.
  5. Restore into a fresh staging directory.
  6. Start without public ports or outbound SMTP.
  7. Verify one test account, folders, a known message, aliases and logs.
  8. Destroy the recovery clone securely and record drill date, duration and fixes.

Docker’s volume documentation describes the general temporary-container archive pattern. Docker Mailserver’s own FAQ provides the DMS-specific paths. The missing operational step is the recovery drill: an archive becomes trustworthy only when a clean environment can use it without touching production.