Docker Mailserver Upgrade to v16: 4 Steps and 3 Mistakes
A docker mailserver upgrade from 15.x to 16.x is not just a new image tag. Version 16.0.0 moves the image from Debian 12 to Debian 13 and from Dovecot 2.3 to Dovecot 2.4. Your mail and accounts survive the switch, but an old dovecot.cf with 2.3 setting names stops Dovecot completely, SA_SPAM_SUBJECT is rejected, and DHE ciphers disappear from ports 587 and 993. The container still reports "up and running". Below is the full upgrade on a test server, what broke and the exact fixes.
Lab: docker-mailserver 15.1.0 upgraded to 16.0.1 on the same volumes, rootless podman 5.8 (the docker commands are identical), a Debian 13 client container with swaks, curl and openssl, October 2026. Every output below comes from that lab. Sources: the docker-mailserver v16.0.0 release, its CHANGELOG and the Dovecot 2.3 to 2.4 upgrade guide.
Docker mailserver upgrade to v16, in short:
- Back up
docker-data/dms/and stop the container. - Rename 2.3 settings in
config/dovecot.cf(for exampledisable_plaintext_authbecomesauth_allow_cleartext). - Rename
SA_SPAM_SUBJECTtoSPAM_SUBJECTinmailserver.env. - Change the image tag to
16.0.1, start it and checkdoveconf -nand an IMAP login, not only the "up and running" line.
Contents
- What changes in docker-mailserver 16
- The test server on 15.1.0
- Step 1: back up before the docker mailserver upgrade
- Step 2: what happens if you only change the image
- Step 3: convert dovecot.cf to Dovecot 2.4 names
- Step 4: start 16.0.1 and verify mail, flags and sending
- DHE ciphers are gone on 587 and 993
- Healthcheck and other details
- Rollback to 15.1.0
- Docker mailserver upgrade checklist
What changes in docker-mailserver 16
Most of the docker mailserver upgrade work comes from the base image change brings new major versions of the main services. Before and after, from the lab:
# docker exec mailserver bash -c 'cat /etc/os-release | grep PRETTY; dovecot --version; postconf -h mail_version'
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
2.3.19.1 (9b53102964)
3.7.11
# docker exec mailserver bash -c 'cat /etc/os-release | grep PRETTY; dovecot --version; postconf -h mail_version' 2>&1
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
2.4.1-4 (7d8c0e5759)
3.10.13
The changes that can break an existing install during a docker mailserver upgrade, according to the changelog:
| Change in v16 | Who is affected |
|---|---|
Dovecot 2.3 to 2.4: "Custom dovecot.cf using 2.3 syntax will not load" | Anyone with config/dovecot.cf or mounted Dovecot files |
SA_SPAM_SUBJECT no longer accepted, use SPAM_SUBJECT | SpamAssassin users with an old env file |
Bundled DH parameters and dhparams.pem override removed, no DHE on 587/465/143/993/110/995 | Very old mail clients that only speak DHE |
LDAP mailStorageDirectory is a path only (no maildir: prefix) | ACCOUNT_PROVISIONER=LDAP |
| Pyzor and Razor removed | SpamAssassin setups that relied on them |
| All DMS logs go to STDERR | Log filters that read only stdout |
The lab covers the first three. The LDAP change was not tested.
The test server on 15.1.0
To make the docker mailserver upgrade realistic, the v15 setup used options that many older installs still carry: SpamAssassin with SA_SPAM_SUBJECT, a dovecot.cf with two common settings and a custom dhparams.pem:
# cat mailserver.env; cat dms/config/dovecot.cf; ls dms/config
SSL_TYPE=manual
SSL_CERT_PATH=/tmp/docker-mailserver/ssl/cert.pem
SSL_KEY_PATH=/tmp/docker-mailserver/ssl/key.pem
ENABLE_SPAMASSASSIN=1
ENABLE_AMAVIS=1
SA_SPAM_SUBJECT=***SPAM***
PERMIT_DOCKER=none
mail_max_userip_connections = 50
disable_plaintext_auth = yes
dhparams.pem
dovecot.cf
ssl
Two accounts, one alias and three messages for user1. Message 2 was flagged so we can see later whether flags survive the docker mailserver upgrade:
# docker exec mailclient curl -sk --url 'imaps://mailserver/INBOX' --user 'user1@example.test:User1-pass-2026' -X 'SEARCH ALL'
* SEARCH 1 2 3
# docker exec mailclient curl -sk --url 'imaps://mailserver/INBOX' --user 'user1@example.test:User1-pass-2026' -X 'STORE 2 +FLAGS (\Seen \Flagged)'; docker exec mailclient curl -sk --url 'imaps://mailserver/INBOX' --user 'user1@example.test:User1-pass-2026' -X 'SEARCH FLAGGED'
* 2 FETCH (FLAGS (\Flagged \Seen))
* SEARCH 2
On 15.1.0 a client could still negotiate a DHE cipher on IMAPS and submission:
# docker exec mailclient bash -c 'echo | openssl s_client -connect mailserver:993 -tls1_2 -cipher DHE-RSA-AES256-GCM-SHA384 2>&1 | grep -E "Cipher is|Server Temp Key|alert|handshake failure" | head -3'
New, TLSv1.2, Cipher is DHE-RSA-AES256-GCM-SHA384
Step 1: back up before the docker mailserver upgrade
Every docker mailserver upgrade should start with a backup. Stop the container cleanly so Postfix and Dovecot flush their queues and indexes, then copy the whole data folder. With rootless podman many files belong to sub-UIDs: in the lab a plain du on mail-state printed "Permission denied" for the Postfix spool, so make the copy as root or with podman unshare.
- Stop the stack with
docker compose down(the lab useddocker stop -t 60 mailserver). - Archive the whole
docker-data/dmsfolder as root, for example withtar -czf. - Write down the image tag from your
compose.yaml.
Pin the old tag (for example 15.1.0) instead of latest, so a rollback is one edit.
Step 2: what happens if you only change the image
This is the trap of a docker mailserver upgrade. With the old config and image: ghcr.io/docker-mailserver/docker-mailserver:16.0.1, the container started and logged that it is up:
# docker logs mailserver 2>&1 | grep -E 'Welcome|SA_SPAM_SUBJECT|Fatal|up and running' | head -8
2026-10-25 13:29:56+00:00 ERROR start-mailserver.sh: 'SA_SPAM_SUBJECT' has been renamed to 'SPAM_SUBJECT' since DMS v16
2026-10-25 13:29:56+00:00 INFO start-mailserver.sh: Welcome to docker-mailserver v16.0.1
doveconf: Fatal: Error in configuration file /etc/dovecot/local.conf line 2: disable_plaintext_auth: Unknown setting: disable_plaintext_auth
2026-10-25 13:29:58+00:00 INFO start-mailserver.sh: mail.example.test is up and running

Dovecot did not start at all. Supervisor kept restarting it, and IMAP was closed:
# docker logs mailserver 2>&1 | grep -c 'exited: dovecot (exit status 89'
12
# docker exec mailclient curl -sk --url 'imaps://mailserver/INBOX' --user 'user1@example.test:User1-pass-2026' -X 'SEARCH ALL'; echo curl rc=$?
curl rc=7
curl exit code 7 means "failed to connect": nothing listened on 993. At the same moment supervisorctl status showed dovecot RUNNING, because it caught one of the restart attempts. Do not trust the "up and running" line or the process list after a docker mailserver upgrade; test a real login.
/etc/dovecot/local.conf in the error is your docker-data/dms/config/dovecot.cf: the DMS Dovecot docs say the file "is copied internally to /etc/dovecot/local.conf". Note also that the SA_SPAM_SUBJECT message is logged as ERROR, but the start continues.
Step 3: convert dovecot.cf to Dovecot 2.4 names
The fix for this docker mailserver upgrade error is in your own config. Check every line of your dovecot.cf against the "Converted Settings" table of the Dovecot upgrade guide. A few entries that often appear in DMS overrides:
| Dovecot 2.3 | Dovecot 2.4 |
|---|---|
disable_plaintext_auth | replaced by auth_allow_cleartext |
ssl_cert / ssl_key | ssl_server_cert_file / ssl_server_key_file |
ssl_dh | ssl_server_dh_file |
quota_grace | quota_storage_grace |
plugin { ... } block | "All plugin settings are now global settings" |
service { service_count } | service_restart_request_count |
Watch the inverted meaning: disable_plaintext_auth = yes becomes auth_allow_cleartext = no. Settings that still exist in 2.4, like mail_max_userip_connections, stay as they are. The fixed file and env:
# cat dms/config/dovecot.cf; grep SPAM_SUBJECT mailserver.env
mail_max_userip_connections = 50
auth_allow_cleartext = no
SPAM_SUBJECT=***SPAM***
Before the real switch, test a new dovecot.cf with doveconf -n inside the 16.0.1 image. Any unknown name prints the same doveconf: Fatal line as above. After the fix it parsed cleanly:
# docker exec mailserver doveconf -n 2>&1 | grep -E 'mail_max_userip|auth_allow_cleartext|ssl_server_dh_file|ssl_server_cert_file'
mail_max_userip_connections = 50
auth_allow_cleartext does not show up because doveconf -n only prints values that differ from the default, and no is already the default.
Step 4: start 16.0.1 and verify mail, flags and sending
With the fixed config the docker mailserver upgrade went through. Accounts, aliases, messages and flags were all there:
# docker exec mailserver setup email list
* user1@example.test ( 4.0K / ~ ) [0%]
[ aliases -> info@example.test ]
* admin@example.test ( 0 / ~ ) [0%]
# docker exec mailclient curl -sk --url 'imaps://mailserver/INBOX' --user 'user1@example.test:User1-pass-2026' -X 'SEARCH ALL'
* SEARCH 1 2 3
# docker exec mailclient curl -sk --url 'imaps://mailserver/INBOX' --user 'user1@example.test:User1-pass-2026' -X 'SEARCH FLAGGED'
* SEARCH 2
# docker exec mailclient curl -sk --url 'imaps://mailserver/INBOX;UID=2' --user 'user1@example.test:User1-pass-2026' | grep -E '^Subject:'
Subject: before upgrade 2
Receiving on port 25 (through the alias) and sending on 587 with STARTTLS and authentication both worked:
# docker exec mailclient swaks --server mailserver --port 25 --helo client.example.org --from tester@gmail.com --to info@example.test --header 'Subject: after upgrade' 2>&1 | grep -E '^<\*\*|250 2.0.0 Ok: queued'
<- 250 2.0.0 Ok: queued as E51E41169
# docker exec mailclient swaks --server mailserver --port 587 --tls --from user1@example.test --to admin@example.test --auth-user user1@example.test --auth-password 'User1-pass-2026' 2>&1 | grep -E '235 2.7.0|250 2.0.0 Ok|^<\*\*' | head -3
<~ 235 2.7.0 Authentication successful
<~ 250 2.0.0 Ok: queued as 2D83F117B
# docker exec mailserver doveadm mailbox status -u user1@example.test messages INBOX; docker exec mailserver doveadm mailbox status -u admin@example.test messages INBOX
INBOX messages=4
INBOX messages=1
The spam subject still arrives in the generated settings under its new name: /etc/dms-settings contained SPAM_SUBJECT='***SPAM***' on both versions.
DHE ciphers are gone on 587 and 993
One docker mailserver upgrade side effect you only notice with old clients: v16 removed the bundled DH parameters (pull request #4562), and the dhparams.pem in the config folder was ignored without a warning. The same DHE handshake that worked on 15.1.0 now fails:
# docker exec mailclient bash -c 'echo | openssl s_client -connect mailserver:993 -tls1_2 -cipher DHE-RSA-AES256-GCM-SHA384 2>&1 | grep -E "Cipher is|Server Temp Key|alert|handshake failure" | head -3'
80D7EBD0A37F0000:error:0A000410:SSL routines:ssl3_read_bytes:ssl/tls alert handshake failure:../ssl/record/rec_layer_s3.c:918:SSL alert number 40
New, (NONE), Cipher is (NONE)
# docker exec mailclient bash -c 'echo | openssl s_client -connect mailserver:993 -tls1_2 2>&1 | grep -E "Cipher is"'
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Port 587 failed the same way; port 25 still negotiated DHE-RSA-AES256-GCM-SHA384, as the changelog says. On the server side the failed client shows up in mail.log as SSL_accept() failed: error:0A0000C1:SSL routines::no shared cipher. Normal clients use ECDHE and are not affected. If an old device needs DHE, follow "Using custom DH parameters" in the DMS v16 SSL docs.
Healthcheck and other details
dms-healthcheckis new in v16 and returnedrc=0on the working server. Under podman,docker inspect -f '{{json .Config.Healthcheck}}'printednull: the changelog notes that podman and Kubernetes must configure the healthcheck explicitly. Setup details are in Docker Mailserver healthcheck in v16.- APT sources inside the image are now in DEB822 format:
/etc/apt/sources.list.d/listsdebian.sourcesandupstream-rspamd.sources. Adjust anyuser-patches.shthat writes.listfiles. which pyzor razor-checkfound nothing (exit 1): both tools are gone.
Rollback to 15.1.0
A docker mailserver upgrade should always have a way back. To test the rollback, the lab put the 2.3 dovecot.cf back and started 15.1.0 on the volumes that 16.0.1 had used. All four messages, including the one delivered by v16, were readable:
# docker exec mailclient curl -sk --url 'imaps://mailserver/INBOX' --user 'user1@example.test:User1-pass-2026' -X 'SEARCH ALL'
* SEARCH 1 2 3 4
So a short rollback worked in this test with Maildir storage. It is still not a replacement for the backup from step 1: restore that if v16 ran for a while and you are not sure.
Docker mailserver upgrade checklist
- Before the docker mailserver upgrade, read the v16 changelog for your features (LDAP, quotas, SpamAssassin, custom TLS).
- Back up
docker-data/dms/, write down the current image tag. - Convert
dovecot.cfand any mounted Dovecot files to 2.4 names; rundoveconf -nin the new image. - Rename
SA_SPAM_SUBJECTtoSPAM_SUBJECT. - Switch to
16.0.1, then grep the logs forFatalandERROR. - Test IMAP login, a mail on port 25 and a send on 587 before you call the docker mailserver upgrade done.
New to DMS? Start with the Docker Mailserver setup guide, which installs 16.0.1 directly.
Comments
Post a Comment