docker-mailserver Docker Compose 部署指南
docker-mailserver(ghcr.io 镜像 16.0.1)Docker Compose 部署指南:compose 逐段解析、端口映射表、数据卷目录、setup.sh 账号/域名/DKIM 初始化实测与常见问题(120 秒建号窗口、restart 与重建差异)。
docker-mailserver Docker Compose 部署指南
> 目标:从零在一台 Ubuntu 服务器上以官方镜像部署 docker-mailserver(DMS)邮件服务器,覆盖 compose 逐段解析、端口映射、数据卷目录、setup.sh 初始化(账号/域名/DKIM)与部署期常见问题。全部命令与回显在 Ubuntu 22.04 + DMS 16.0.1 实测通过,数据目录 /opt/data/docker-mailserver/。
一、环境信息
- 操作系统:Ubuntu 22.04(内核 6.8.x,x86_64)
- Docker:29.8.1,Compose 插件 v2.40.3(
docker compose version实测) - docker-mailserver:16.0.1(
ghcr.io/docker-mailserver/docker-mailserver:16.0.1) - 邮件域名:
example.com,邮件主机名:mail.example.com(需有对应 DNS A 记录与 MX 记录) - 服务端口(宿主机实测监听):
$ ss -ltn | grep -E ':(13925|14587|14143)\s'
LISTEN 0 4096 0.0.0.0:14143 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:13925 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:14587 0.0.0.0:*
LISTEN 0 4096 [::]:14143 [::]:*
LISTEN 0 4096 [::]:13925 [::]:*
LISTEN 0 4096 [::]:14587 [::]:*
- 数据目录:
/opt/data/docker-mailserver/(mail-data 邮件存储 / state 状态与内部数据库 / config 账号与覆盖配置,另有 logs 日志与 ssl 证书两个可选目录)
二、镜像选型说明
docker-mailserver 官方镜像有两个仓库地址,选型结论如下(2026-10-18 manifest 实查):
| 仓库 | 实测结论 | 结论 |
|---|---|---|
ghcr.io/docker-mailserver/docker-mailserver | 16.0.1 标签存在(docker manifest inspect 通过) | 推荐,必选 |
docker.io/mailserver/docker-mailserver | :latest 与 :16.0.1 均 manifest inspect 失败(Hub tag MISSING) | 不可用(旧命名空间已无有效标签) |
版本标签策略:固定使用版本号标签(如 16.0.1)而非 latest,升级时显式改标签重建,避免不可控漂移。拉取与确认:
docker pull ghcr.io/docker-mailserver/docker-mailserver:16.0.1
启动后版本自报(实测日志首行):
2026-10-18 08:47:40+08:00 INFO start-mailserver.sh: Welcome to docker-mailserver v16.0.1
三、前置条件
- DNS 记录(部署前在域名服务商处确认):
- A 记录:
mail.example.com → 服务器公网 IP; - MX 记录:
example.com → mail.example.com(优先级 10); - SPF/DMARC/DKIM 记录在第四章 DKIM 生成后补齐(记录值实测生成,见 4.5)。
- A 记录:
- 端口规划:本指南采用非标准高位端口映射(
25→13925、587→14587、143→14143),适配 25 端口被云厂商限制或已有服务占用的情况。生产环境若可直用标准端口,把映射左侧改为25/587/143/465/993即可。 - 检查端口占用:
ss -ltn | grep -E ':(13925|14587|14143)\s' || echo 'ports free'
部署前实测:ports 13925/14587/14143 free(无输出即空闲)。
4. 创建数据目录:
mkdir -p /opt/data/docker-mailserver/{mail-data,state,config}
- 下载官方管理脚本与配置模板(与镜像版本同 tag):
curl -fLso /opt/data/docker-mailserver/setup.sh \
https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/v16.0.1/setup.sh
chmod a+x /opt/data/docker-mailserver/setup.sh
curl -fLso /opt/data/docker-mailserver/mailserver.env \
https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/v16.0.1/mailserver.env
四、部署步骤
4.1 编写 docker-compose.yml
完整文件(本指南实测所用,逐段解析见 4.2):
services:
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:16.0.1
container_name: dms-16.0.1-mail
hostname: mail.example.com
env_file: mailserver.env
ports:
- "13925:25" # SMTP (STARTTLS)
- "14587:587" # ESMTP submission (STARTTLS)
- "14143:143" # IMAP4 (STARTTLS)
volumes:
- ./mail-data/:/var/mail/
- ./state/:/var/mail-state/
- ./config/:/tmp/docker-mailserver/
- ./logs/:/var/log/mail/
- ./ssl/:/etc/dms/tls/
- /etc/localtime:/etc/localtime:ro
cap_add:
- NET_ADMIN
dns:
- 192.168.112.53
restart: always
stop_grace_period: 1m
4.2 逐段解析
image:固定版本标签 16.0.1,选型依据见第二章;container_name:dms-16.0.1-mail,便于日志与命令定位;hostname:必须为邮件主机名(mail.example.com),Postfix/Dovecot 的 EHLO、证书校验、日志主机名都取自它;env_file:加载官方mailserver.env(第 4.3 节调整);ports:三组映射,对应 SMTP 收信、认证提交、IMAP 收信;465(SMTPS)、993(IMAPS)、995(POP3S)端口在镜像内已监听(见第 4.6 端口表),如需对外暴露自行加映射;volumes:五个挂载点——邮件存储、内部状态、账号配置、日志持久化、TLS 证书,全部 bind mount 到宿主机(第五章);cap_add: NET_ADMIN:仅当启用 fail2ban(ENABLE_FAIL2BAN=1)时必需(封禁靠防火墙规则实现),未启用可删;dns:本环境指向内部 DNS192.168.112.53(发布测试域 SPF/DKIM/DMARC 记录的 dnsmasq);生产环境删除此段,使用宿主机默认解析即可;restart: always:开机自启与异常自动拉起;stop_grace_period: 1m:给 Postfix/Dovecot 一分钟优雅排空在途连接,避免强杀丢信。
4.3 调整 mailserver.env(本指南实测值)
从官方模板修改以下键(默认值与逐参数实测见《docker-mailserver 16.0.1 配置参数详解》):
TZ=Asia/Shanghai
ENABLE_RSPAMD=1
ENABLE_CLAMAV=1
ENABLE_OPENDKIM=0
ENABLE_OPENDMARC=0
ENABLE_POLICYD_SPF=0
ENABLE_AMAVIS=0
⚠️ 实测注意:模板默认
ENABLE_RSPAMD=0且旧栈(Amavis/OpenDKIM/OpenDMARC/policyd-spf)为 1。实测同开两套栈时启动日志明确警告并存不推荐,DKIM/SPF/DMARC 应全部交由 Rspamd 承担,旧栈四项全部置 0。完整实测证据(警告原文与消除过程)见《docker-mailserver 安全加固指南》。
4.4 启动与验证
cd /opt/data/docker-mailserver
docker compose up -d
启动状态实测(docker compose ps):
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
dms-16.0.1-mail ghcr.io/docker-mailserver/docker-mailserver:16.0.1 "/usr/bin/dumb-init …" mailserver 16 minutes ago Up 16 minutes (healthy) 110/tcp, 465/tcp, 993/tcp, 995/tcp, 4190/tcp, 0.0.0.0:13925->25/tcp, [::]:13925->25/tcp, 0.0.0.0:14143->143/tcp, [::]:14143->143/tcp, 0.0.0.0:14587->587/tcp, [::]:14587->587/tcp
启动完成日志实测(docker logs dms-16.0.1-mail 节选):
2026-10-18 08:47:40+08:00 INFO start-mailserver.sh: Welcome to docker-mailserver v16.0.1
2026-10-18 08:47:42+08:00 INFO start-mailserver.sh: mail.example.com is up and running
看到 up and running 即部署完成。(healthy) 状态来自镜像内置健康检查(dms healthcheck),首次启动约需 30~60 秒。
4.5 setup.sh 初始化(账号 / 域名 / DKIM)
初始化全部通过 setup.sh 完成(命令在宿主机数据目录执行,脚本自动连接运行中的服务)。
第一步:创建邮箱账号(首次创建账号会自动登记邮件域 example.com):
./setup.sh email add admin@example.com q1w2e3r4
./setup.sh email add user@example.com q1w2e3r4
实测:两条命令静默返回(退出码 0,无输出)。列出账号确认(./setup.sh email list 实测回显):
* admin@example.com ( 8.0K / ~ ) [0%]
[ aliases -> info@example.com ]
* user@example.com ( 5.0K / 1.0G ) [0%]
(~ 表示未设配额;user@example.com 的 1.0G 来自后续执行的 ./setup.sh quota set user@example.com 1G。)
第二步:生成 DKIM 密钥:
./setup.sh config dkim
实测结果:在 config/rspamd/dkim/ 下生成 RSA-2048 密钥对(选择器 mail),同时打印对应 DNS TXT 记录全文。生成文件实测清单:
config/rspamd/dkim/rsa-2048-mail-example.com.private.dns.txt
config/rspamd/dkim/rsa-2048-mail-example.com.public.dns.txt
config/rspamd/dkim/mail.example.com.key
config/rspamd/dkim/mail.example.com.pub
把 public.dns.txt 中的 mail._domainkey.example.com TXT 记录发布到 DNS 后,DKIM 签名即可被外部验证(收发实测与头字段验证见《docker-mailserver 安全加固指南》)。
第三步:别名与配额(按需):
./setup.sh alias add info@example.com admin@example.com # 别名投递到 admin
./setup.sh quota set user@example.com 1G # 1G 配额
别名写入 config/postfix-virtual.cf(实测文件内容:info@example.com admin@example.com),投递改写实测日志:
2026-10-18T08:59:43.724810+08:00 mail postfix/lmtp[2442]: D91AA9005FF: to=<admin@example.com>, orig_to=<info@example.com>, relay=mail.example.com[/var/run/dovecot/lmtp], delay=0.87, delays=0.85/0.01/0.01/0.01, dsn=2.0.0, status=sent (250 2.0.0 <admin@example.com> eIbZKv8Z1GqLCQAAYIgGOA Saved)
4.6 端口表
| 容器端口 | 协议用途 | 宿主机映射(本指南) | TLS 形态(实测) |
|---|---|---|---|
| 25 | SMTP 收信 | 13925 | STARTTLS(配置证书后出现在 EHLO 能力中) |
| 587 | ESMTP 认证提交 | 14587 | STARTTLS + AUTH LOGIN |
| 143 | IMAP4 | 14143 | STARTTLS |
| 465 | SMTPS 提交 | 未映射(按需添加) | 隐式 TLS |
| 993 | IMAPS | 未映射(按需添加) | 隐式 TLS(配置证书后实测监听) |
| 995 | POP3S | 未映射(默认未启用 POP3) | 隐式 TLS |
| 110 | POP3 | 未映射(默认未启用) | STARTTLS |
| 4190 | ManageSieve | 未映射(按需添加) | — |
⚠️ 143/465/993 等端口在
docker compose ps的 PORTS 列中始终显示(镜像 EXPOSE),对外是否可达取决于是否写ports映射与防火墙放行。
五、数据持久化说明
五个挂载目录与实测占用(du -sh 实测,启用 Rspamd + ClamAV 运行一阶段后):
| 目录 | 容器内路径 | 内容 | 实测占用 |
|---|---|---|---|
mail-data/ | /var/mail/ | 邮件存储(Maildir,按域名/账号分目录) | 192K |
state/ | /var/mail-state/ | 各组件内部状态与数据库 | 112M |
config/ | /tmp/docker-mailserver/ | 账号/别名/配额数据 + 覆盖配置 + DKIM 密钥 | 56K |
logs/ | /var/log/mail/ | mail.log 等日志持久化 | 404K |
ssl/ | /etc/dms/tls/ | TLS 证书与私钥 | 20K |
state/ 实测目录清单(各组件状态隔离存放,重建不丢状态):
lib-amavis lib-clamav lib-dovecot lib-fail2ban lib-logrotate
lib-postfix lib-redis lib-rspamd spool-postfix
邮件目录结构实测示例:mail-data/example.com/admin/new/ 下为单封邮件文件(Maildir 格式),Junk 等子文件夹为 .Junk/ 隐藏目录。
六、环境变量表
以下为部署决策相关核心键(完整键集见官方 mailserver.env 模板,逐键实测与改值对比见《docker-mailserver 16.0.1 配置参数详解》):
| 变量 | 默认 | 本指南取值 | 作用(实测依据) |
|---|---|---|---|
TZ | 空 | Asia/Shanghai | 日志时区(实测日志带 +08:00) |
ENABLE_RSPAMD | 0 | 1 | 启用 Rspamd 反垃圾 + DKIM/SPF/DMARC |
ENABLE_CLAMAV | 0 | 1 | 启用病毒扫描 |
ENABLE_AMAVIS | 1 | 0 | 旧内容过滤网关,Rspamd 启用后关闭 |
ENABLE_OPENDKIM | 1 | 0 | 旧 DKIM 栈,交由 Rspamd |
ENABLE_OPENDMARC | 1 | 0 | 旧 DMARC 栈,交由 Rspamd |
ENABLE_POLICYD_SPF | 1 | 0 | 旧 SPF 栈,交由 Rspamd |
SSL_TYPE | 空 | manual | 证书接入方式(letsencrypt/manual/self-signed) |
SSL_CERT_PATH | 空 | /etc/dms/tls/fullchain.pem | 证书路径(配合 manual) |
SSL_KEY_PATH | 空 | /etc/dms/tls/privkey.pem | 私钥路径(配合 manual) |
ENABLE_FAIL2BAN | 0 | 1 | 认证爆破封禁(需 NET_ADMIN 能力) |
SPOOF_PROTECTION | 空 | 1 | 禁止认证身份与发件地址不匹配(实测 553 拒信) |
PERMIT_DOCKER | none | none | 信任网段控制,保持 none 防开放中继 |
POSTMASTER_ADDRESS | 空 | 空(按域名默认) | 管理员邮箱 |
MOVE_SPAM_TO_JUNK | 1 | 1 | 垃圾自动归档 Junk(实测生效) |
七、配置参数说明
配置体系分三层:mailserver.env 声明式总开关(第六章)→ config/ 下账号与域数据文件(setup.sh 自动维护,手工不编辑)→ 细粒度覆盖文件(config/postfix-main.cf、config/dovecot.cf、config/rspamd/override.d/)。第三层的改值→重启→行为对比实测、两层配置热加载边界(哪些文件改动即时生效、哪些必须重建)详见《docker-mailserver 16.0.1 配置参数详解》。
八、配置文件挂载
config/ 挂载到容器 /tmp/docker-mailserver/(这是官方约定路径,文档与工具均按此路径引用)。部署完成后实测目录内容:
postfix-accounts.cf # 账号哈希(email add 自动写入)
postfix-virtual.cf # 别名表(alias add 自动写入)
dovecot-quotas.cf # 配额表(quota set 自动写入)
rspamd/dkim/ # DKIM 密钥与 DNS 记录(config dkim 生成)
rspamd/override.d/ # Rspamd 覆盖配置(可选,手工)
postfix-main.cf # Postfix 主配置覆盖(可选,手工)
dovecot.cf # Dovecot 配置覆盖(可选,手工)
⚠️ 这些文件随宿主机目录持久化,升级重建后账号/别名/密钥全部保留;
postfix-accounts.cf存的是口令哈希,禁止明文备份外传。
九、等价 docker run
与第五章 compose 等价的单命令启动形态(官方 README 同款写法,本指南实测通过;--env-file、卷路径按实际值填写):
docker run -d --name dms-16.0.1-run \
--hostname mail.example.com \
--env-file /opt/data/docker-mailserver/mailserver.env \
-v /opt/data/docker-mailserver-equiv/mail-data/:/var/mail/ \
-v /opt/data/docker-mailserver-equiv/state/:/var/mail-state/ \
-v /opt/data/docker-mailserver-equiv/config/:/tmp/docker-mailserver/ \
-v /opt/data/docker-mailserver/ssl/:/etc/dms/tls/:ro \
-v /etc/localtime:/etc/localtime:ro \
--restart always --stop-timeout 60 \
ghcr.io/docker-mailserver/docker-mailserver:16.0.1
实测启动日志(全新空数据目录):
2026-10-18 09:11:54+08:00 INFO start-mailserver.sh: Welcome to docker-mailserver v16.0.1
2026-10-18 09:11:54+08:00 WARN start-mailserver.sh: You need at least one mail account to start Dovecot (120s left for account creation before shutdown)
倒计时窗口内创建账号后服务完成启动(实测 docker exec -i dms-16.0.1-run setup email add demo@example.com temp12345 后):
2026-10-18 09:13:16+08:00 INFO start-mailserver.sh: mail.example.com is up and running
十、常用运维命令
docker compose ps # 状态与健康检查(实测 healthy)
docker logs -f dms-16.0.1-mail # 实时日志(含每个账号创建、每封收发)
docker compose restart mailserver # 重启(仅重启进程,不重新应用主配置覆盖)
docker compose up -d --force-recreate # 重建实例(应用 postfix-main.cf/dovecot.cf 等主配置)
./setup.sh help # 管理命令总览
setup.sh 完整命令树(./setup.sh help 实测节选):
COMMAND := { email | alias | quota | dovecot-master | config | relay | debug } SUBCOMMAND
[SUB]COMMANDS
COMMAND email :=
setup email add <EMAIL ADDRESS> [<PASSWORD>]
setup email update <EMAIL ADDRESS> [<PASSWORD>]
setup email del [ OPTIONS... ] <EMAIL ADDRESS> [ <EMAIL ADDRESS>... ]
setup email restrict <add|del|list> <send|receive> [<EMAIL ADDRESS>]
setup email list
COMMAND alias :=
setup alias add <EMAIL ADDRESS> <RECIPIENT>
setup alias del <EMAIL ADDRESS> <RECIPIENT>
setup alias list
COMMAND quota :=
setup quota set <EMAIL ADDRESS> [<QUOTA>]
setup quota del <EMAIL ADDRESS>
COMMAND dovecot-master :=
setup dovecot-master add <USERNAME> [<PASSWORD>]
setup dovecot-master update <USERNAME> [<PASSWORD>]
setup dovecot-master del [ OPTIONS... ] <USERNAME> [ <EMAIL ADDRESS>... ]
setup dovecot-master list
COMMAND config :=
setup config dkim [ ARGUMENTS... ]
COMMAND relay :=
setup relay add-auth <DOMAIN> <USERNAME> [<PASSWORD>]
日常运维(巡检/排障/备份/升级)的完整实操见《docker-mailserver 日常运维手册》,安全加固见《docker-mailserver 安全加固指南》。
十一、常见问题
Q1:全新部署启动后 120 秒后自动退出?
这是设计行为:空账号环境启动时进入建号倒计时(实测日志 You need at least one mail account to start Dovecot (120s left for account creation before shutdown))。在窗口期内执行 ./setup.sh email add <地址> <密码> 即正常完成启动(见第九章实测流程)。
Q2:改了 config/postfix-main.cf 或 config/dovecot.cf 重启后不生效?
实测确认:docker compose restart 会跳过大部分初始化(日志实测:Container was restarted. Skipping most setup routines.),主配置覆盖文件只在实例重建时应用。改这两类文件后必须执行 docker compose up -d --force-recreate。实测对比:message_size_limit 默认 10240000,改写覆盖文件后 restart 无效,--force-recreate 后 postconf message_size_limit 返回 20480000、EHLO 能力同步显示 250-SIZE 20480000。
Q3:email list 对新账号报错?
实测:刚创建、尚未收到第一封邮件的账号执行 ./setup.sh email list 会输出:
doveadm(third@example.com): Error: cmd quota get: User doesn't exist
2026-10-18 08:58:58+08:00 ERROR listmailuser: Supplied non-number argument '' to '_bytes_to_human_readable_size()'
* third@example.com ( / ) [%]
mailbox 目录生成后(收到第一封邮件)自动恢复正常,属无害告警。
Q4:启动日志警告 Rspamd 与 Amavis/OpenDKIM/OpenDMARC/policyd-spf 并存?
v16 推荐 DKIM/SPF/DMARC 与评分全部由 Rspamd 承担,把 ENABLE_AMAVIS/ENABLE_OPENDKIM/ENABLE_OPENDMARC/ENABLE_POLICYD_SPF 全部置 0 后警告消失(实测)。
Q5:外部发件域不可解析时收不到信?
实测日志:450 4.1.8 <friend@external.test>: Sender address rejected: Domain not found——这是 Postfix 对发件域做 DNS 校验的默认行为(reject_unknown_sender_domain),属正常防护,请使用真实可解析的域名发信测试。
Q6:对外发送被拒 Relay access denied?
实测:来自非信任来源、收件人非本域的请求一律拒绝(实测日志 554 5.7.1 <nobody@dead.test>: Relay access denied),这是防开放中继的正确行为;对外投递请走 587/465 端口认证提交。
Q7:云服务器收不到 25 端口入站?
多数云厂商默认封禁入站 25。可先用本指南的高位端口映射跑通功能,再向厂商申请解封;或保留高位映射并配合中继(setup relay)出站。
十二、参考链接
- 官方部署文档:
https://docker-mailserver.github.io/docker-mailserver/latest/usage/ - Compose 示例:
https://github.com/docker-mailserver/docker-mailserver/blob/master/compose.yaml - 环境变量说明:
https://docker-mailserver.github.io/docker-mailserver/latest/config/environment/ - setup.sh 下载页(按 tag 取对应版本):
https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/v16.0.1/setup.sh - 官方 FAQ:
https://docker-mailserver.github.io/docker-mailserver/latest/faq/