Courier MTA 1.0.16 Docker Compose 部署指南
Courier MTA 1.0.16 Docker Compose 部署指南:无官方镜像前提下的 ubuntu:22.04+deb 自建镜像形态,entrypoint 修三大坑(me 缺失/bind mount 遮蔽队列目录/netbase 缺 /etc/services)、courierd 就绪等待、四端口验收、587 STARTTLS AUTH 实测与 restart 持久化验证。实测日期 2026-10-25。
Courier MTA 1.0.16 Docker Compose 部署指南
实测日期 2026-10-25。镜像内为 Ubuntu 22.04 + 官方 deb(courier-mta 1.0.16-3build3)。示例域
quzuo-test.cn,示例账号admin/q1w2e3r4。
一、先说清楚:没有官方镜像
Docker Hub 实查:Courier 官方不发布任何 Docker 镜像,搜到的社区镜像(如 camptocamp/courier-imap)多年未更新、星数寥寥,不可作为生产基础。因此本文采用**「ubuntu:22.04 基础镜像 + 内装官方 deb」的自建镜像 Compose 形态**——版本与 Ubuntu 22.04 安装指南完全一致,升级路径清晰。
二、项目结构
courier-compose/
├── Dockerfile
├── entrypoint.sh
└── compose.yaml
2.1 Dockerfile
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
courier-mta courier-imap courier-pop courier-authdaemon \
ca-certificates netbase \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 25 110 143 465 587 993 995
ENTRYPOINT ["/entrypoint.sh"]
netbase 不能省:
--no-install-recommends不带 netbase,容器内没有/etc/services,couriertcpd 解析端口名smtp会报Invalid port: smtp导致 25 端口起不来。这是实测踩出来的坑。
2.2 entrypoint.sh
容器没有 systemd,用 init 脚本逐个拉起各守护进程;有几个「空的 bind mount 会遮蔽镜像内目录」的坑必须处理:
#!/bin/bash
set -e
: "${MAIL_DOMAIN:=quzuo-test.cn}"
: "${ADMIN_PASSWORD:=q1w2e3r4}"
echo "mail.${MAIL_DOMAIN}" > /etc/mailname
# /etc/courier/me 由 postinst 依据 /etc/mailname 生成;镜像构建期没有 mailname,需在此补齐
[ -s /etc/courier/me ] || cp /etc/mailname /etc/courier/me
# 开启提交端口(587)与 SMTP AUTH(TLS 会话内)
sed -i 's/^ESMTPDSTART=NO/ESMTPDSTART=YES/' /etc/courier/esmtpd-msa
sed -i 's/^ESMTPAUTH_TLS=""/ESMTPAUTH_TLS="PLAIN LOGIN CRAM-MD5 CRAM-SHA1 CRAM-SHA256"/' /etc/courier/esmtpd
# 补 postmaster 别名(Debian 默认为空,postmaster 邮件会被丢弃)
sed -i 's/^postmaster:$/postmaster: admin/' /etc/courier/aliases/system
makealiases
# 演示用户(幂等)
if ! id admin >/dev/null 2>&1; then
useradd -m -s /bin/bash admin
echo "admin:${ADMIN_PASSWORD}" | chpasswd
su - admin -c "maildirmake /home/admin/Maildir"
fi
# 数据卷初始化与属主修正:空的 bind mount 会遮蔽镜像内已建好的队列目录,
# courierd 找不到 tmp/msgs/msgq 会静默退出,这里按 Debian 布局补齐
for d in tmp msgs msgq; do mkdir -p "/var/lib/courier/$d"; chmod 750 "/var/lib/courier/$d"; done
chown -R courier:courier /var/lib/courier
# 无 systemd 环境下用 init 脚本逐个拉起各守护进程
/etc/init.d/courier-authdaemon start
courier start
# courierd 需要先完成初始化(建 /run/courier 与队列目录),否则紧随其后的 esmtpd 启动会失败
i=0
while ! pgrep -x courierd >/dev/null 2>&1; do
i=$((i+1)); [ "$i" -gt 10 ] && break; sleep 1
done
sleep 1
/usr/sbin/esmtpd start
/usr/sbin/esmtpd-msa start
/etc/init.d/courier-imap start
/etc/init.d/courier-pop start
echo "Courier stack up: smtp=25 msa=587 imap=143 pop3=110"
# 前台保活并跟随邮件日志
touch /var/log/mail.log
tail -F /var/log/mail.log
三个易踩的坑都在脚本里修了,展开说:
/etc/courier/me缺失:deb 的 postinst 在安装时依据/etc/mailname生成me(本机规范主机名),而构建镜像时没有 mailname,运行时必须补。- 队列目录被遮蔽:镜像里 postinst 建好的
/var/lib/courier/{tmp,msgs,msgq}会被空的 bind mount 整个盖住,courierd 找不到队列目录静默退出(无报错、无日志),必须运行时补齐目录并chown courier:courier。 - 启动顺序:
courier start(courierd)需要先跑起来建好运行时目录,再启动 esmtpd/msa,否则 esmtpd 启动失败。脚本用pgrep -x courierd循环等待(注意必须用-x精确匹配,pgrep -f会把包含 courierd 字样的 shell 命令行本身误判为存活)。
2.3 compose.yaml
name: courier-1.0-stack
services:
courier:
build: .
hostname: mail.quzuo-test.cn
container_name: courier-1.0-mta
environment:
MAIL_DOMAIN: quzuo-test.cn
ADMIN_PASSWORD: q1w2e3r4
ports:
- "8134:25" # SMTP
- "8135:587" # Submission
- "8136:143" # IMAP
- "8137:110" # POP3
volumes:
- ./data/courier:/var/lib/courier # 队列与过滤器状态
- ./data/home:/home # 用户 Maildir
restart: unless-stopped
宿主机 8134–8137 是本专栏实验槽位端口,自行替换为 25/587/143/110 或你的规划端口。465/993/995(SSL 端口)容器内已 EXPOSE,需要时加映射即可。
三、启动与验收
docker compose build
docker compose up -d
$ docker compose ps
NAME STATUS PORTS
courier-1.0-mta Up 25 seconds 465/tcp, 993/tcp, 995/tcp, 0.0.0.0:8134->25/tcp, [::]:8134->25/tcp, 0.0.0.0:8137->110/tcp, [::]:8137->110/tcp, 0.0.0.0:8136->143/tcp, [::]:8136->143/tcp, 0.0.0.0:8135->587/tcp, [::]:8135->587/tcp
容器内四端口与 courierd 验收:
courierd-ALIVE
port 25 OPEN
port 110 OPEN
port 143 OPEN
port 587 OPEN
587 STARTTLS + AUTH 实测(AGFkbWluAHExdzJlM3I0 = \0admin\0q1w2e3r4 的 Base64):
$ openssl s_client -connect 127.0.0.1:587 -starttls smtp
EHLO t
250-mail.quzuo-test.cn Ok.
250-AUTH PLAIN LOGIN CRAM-MD5 CRAM-SHA1 CRAM-SHA256
250-XVERP=Courier
250-XEXDATA
250-XSECURITY=NONE,STARTTLS
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250-SIZE
250 DSN
AUTH PLAIN AGFkbWluAHExdzJlM3I0
235 Ok
收发信实测:
docker exec courier-1.0-mta bash -c 'echo -e "Subject: b2-persist-check\n\npersist body" | sendmail admin'
数秒后邮件落到 ./data/home/admin/Maildir/new/:
1792904217.M364955P276V0000000000000802I00000000005A1C61_1.mail.quzuo-test.cn,S=438
Subject: b2-persist-check
四、持久化验证
邮件队列与 Maildir 都在 bind mount 上,restart 后数据无损:
$ docker compose restart
Container courier-1.0-mta Started
courierd-ALIVE-AFTER-RESTART
1 ← Maildir/new 里邮件数量不变
Subject: b2-persist-check
port 25 OPEN
port 143 OPEN
port 587 OPEN
port 110 OPEN
五、运维提示
- 日志:容器内没有 syslog 守护,courier 的 syslog 输出看不到;排障直接进容器跑命令验证(
mailq、courier show all),或自行在镜像里加 rsyslog。 - 加用户:进容器
useradd+maildirmake,或在 entrypoint 里扩展用户列表循环。 - 改配置:
/etc/courier/*在容器层,改完docker compose restart;想持久化配置就把对应文件也 bind mount 出来。 - 升级:重建镜像即升级(
docker compose build --no-cache && docker compose up -d),数据卷不动。 - 清理:
docker compose down,镜像courier-1.0-stack-courier用docker rmi删除。
六、参考来源
- Ubuntu 22.04 裸机同款安装:《Courier MTA 1.0.16 Ubuntu 22.04 安装指南》
- Courier 官方文档:https://www.courier-mta.org/
- Docker Compose 规范:https://docs.docker.com/compose/compose-file/