Mailu Docker Compose 部署指南
Mailu 2024.06.65 在 Ubuntu 22.04 的 Docker Compose 全链路部署指南:端口映射改造(8128-8132 段)、mailu.env 关键参数、初始管理员自动创建、SMTP(25)/SMTPS(465)/Submission(587)/IMAPS(993) 协议级收发实测与常见问题排障。
Mailu Docker Compose 部署指南
目标:在 Ubuntu 22.04 服务器上以 Docker Compose 完整部署 Mailu 2024.06.65 邮件服务器套件(front/smtp/imap/antispam/admin/resolver/redis 七服务),完成初始管理员创建、邮件域与用户开通,并对 SMTP(25)/SMTPS(465)/Submission(587)/IMAPS(993) 做协议级收发实测。全流程已在 Ubuntu 22.04(x86_64)实测通过,共享主机环境使用 8128–8132 端口段映射(宿主经典邮件端口已被系统策略保留,映射细节见第八章)。
一、环境信息
- 操作系统:Ubuntu 22.04(x86_64)
- Docker:29.8.1,含 compose 插件(
docker compose version→ Docker Compose version v5.5.1) - Mailu:2024.06.65(2024.06 稳定分支最新锁定版,GitHub Releases 实查,发布日期 2026-10-20)
- 部署目录:
/opt/data/quzuo/tmp/mailu/compose/(编排文件与mailu.env同目录) - 数据目录:
/opt/data/quzuo/tmp/mailu/data/(bind mount 持久化,见第五章) - 邮件主机名:
mail.example.com;邮件主域:example.com(内网实验环境用 hosts/内网 DNS 解析,公网环境用 DNS A 记录 + MX 记录,见第三章) - 端口规划:宿主侧使用 8128–8132 五个端口映射邮件服务(
8128→25、8129→465、8130→587、8131→993、8132→443),其余端口省略(详见第八章) - 服务栈网络:自定义网桥
10.89.56.0/24(Mailu 的SUBNET必须与其一致) - 初始管理员:
admin@example.com,密码q1w2e3r4(mailu.env的INITIAL_ADMIN_*自动创建) - 部署前资源复核:
$ free -h
total used free shared buff/cache available
Mem: 31Gi 8.7Gi 1.6Gi 53Mi 20Gi 21Gi
官方最低要求为"不启用 ClamAV 时 1GB 内存 + 1GB 交换分区"(mailu.io/2024.06/compose/requirements.html);本部署 ANTIVIRUS=none,21GiB 可用余量充足。
二、镜像选型说明
Mailu 官方以多服务协作栈发行,每个服务角色由官方维护的独立服务单元承担,均在 GitHub Container Registry(ghcr.io)发布,仓库为 ghcr.io/mailu/<角色>。本部署的镜像清单与实测大小:
$ docker image ls --format '{{.Repository}}:{{.Tag}}\t{{.Size}}' | grep -E 'mailu|redis' | sort
ghcr.io/mailu/admin:2024.06.65 362MB
ghcr.io/mailu/dovecot:2024.06.65 441MB
ghcr.io/mailu/nginx:2024.06.65 415MB
ghcr.io/mailu/postfix:2024.06.65 359MB
ghcr.io/mailu/rspamd:2024.06.65 432MB
ghcr.io/mailu/unbound:2024.06.65 358MB
redis:alpine 161MB
选型要点:
- 锁定版 tag:官方同分支提供两种 tag——主版本号(
2024.06,随分支滚动更新)与锁定版(2024.06.65,固定到某次发布)。生产与实验环境建议用锁定版,避免"重新拉起服务后行为悄然变化"。本文统一锁定2024.06.65。 - 角色与底层软件对照:
nginx(front 统一入口)、postfix(smtp)、dovecot(imap)、rspamd(antispam)、admin(Flask 管理后台+API)、unbound(DNSSEC 递归解析,必选)、redis:alpine(缓存/限速计数,唯一非 ghcr.io/mailu 依赖)。 - 本部署裁剪了可选项:Webmail(
WEBMAIL=none)、ClamAV(ANTIVIRUS=none)、WebDAV(WEBDAV=none)、fetchmail(FETCHMAIL_ENABLED=False)——这些角色默认就不在启动集合里,开启方式见环境变量表。
⚠️ 注意:Mailu 2024.06 的 front 默认只暴露
25,80,443,465,993,995,4190(PORTS默认值),Submission(587)/IMAP(143)/POP3(110) 需显式加进PORTS才会启用——本文第四章已处理,踩坑实录见第十章。
三、前置条件
3.1 基础软件
Docker ≥ 24.0 与 compose 插件:
$ docker version --format '{{.Server.Version}}'
29.8.1
$ docker compose version
Docker Compose version v5.5.1
3.2 域名解析
公网部署需要四类 DNS 记录:mail.example.com 的 A 记录指向服务器、example.com 的 MX 记录指向 mail.example.com、example.com 的 SPF TXT 记录(v=spf1 mx ~all)、DKIM 公钥 TXT 记录(部署后在管理台/API 读取,见本专栏《Mailu 2024.06.65 介绍与选型指南》与《Mailu 2024.06.65 配置参数详解》)。
内网/实验环境无公网 DNS,客户端侧用 hosts 映射等效实现(本文客户端为同网段一台 Ubuntu 22.04 客户机 192.168.146.0/24 网段):
# 客户机上执行(内容追加到 /etc/hosts)
192.168.146.128 mail.example.com
服务栈内部各角色互访使用服务名(front/smtp/imap/admin 等)自动解析,不依赖 hosts。
3.3 端口规划
本机为共享主机,经典邮件端口不可直接绑宿主,统一改用 8128–8132 端口段做映射。部署前先确认无占用:
$ ss -ltn | grep -E ':(8128|8129|8130|8131|8132)\b' || echo '端口段 8128-8132 空闲'
端口段 8128-8132 空闲
3.4 目录规划
$ mkdir -p /opt/data/quzuo/tmp/mailu/compose \
/opt/data/quzuo/tmp/mailu/data/{certs,redis,mail,mailqueue,filter,dkim,main} \
/opt/data/quzuo/tmp/mailu/data/overrides/{nginx,dovecot,postfix,rspamd}
$ cd /opt/data/quzuo/tmp/mailu/compose
四、部署步骤
4.1 准备 TLS 证书(TLS_FLAVOR=cert)
Mailu 的 TLS 策略由 mailu.env 的 TLS_FLAVOR 决定(可取 letsencrypt/mail-letsencrypt/cert/mail/notls)。本文采用 cert(自带证书)模式:front 从 /certs 目录读取 cert.pem/key.pem。实验环境用自签证书模拟;公网生产可直接放置 Let's Encrypt 或商业证书的对应文件。
$ cd /opt/data/quzuo/tmp/mailu/data/certs
$ openssl req -new -x509 -days 3650 -nodes \
-out cert.pem -keyout key.pem \
-subj "/CN=mail.example.com/O=Mailu Demo" \
-addext "subjectAltName=DNS:mail.example.com,IP:127.0.0.1"
$ openssl x509 -in cert.pem -noout -subject -ext subjectAltName
subject=CN = mail.example.com, O = Mailu Demo
X509v3 Subject Alternative Name:
DNS:mail.example.com, IP Address:127.0.0.1
证书文件名必须是 cert.pem/key.pem(可用 TLS_CERT_FILENAME/TLS_KEYPAIR_FILENAME 改名);私钥权限收紧为 600:
$ chmod 600 key.pem && ls -l
total 8
-rw-r--r-- 1 root root 1237 10月 21 08:39 cert.pem
-rw------- 1 root root 1704 10月 21 08:39 key.pem
4.2 编写 mailu.env
在 /opt/data/quzuo/tmp/mailu/compose/mailu.env 写入以下内容(基于官方 setup 向导生成模板,逐项说明见代码块后注释;参数全集详解见本专栏《Mailu 2024.06.65 配置参数详解》):
# ============ 通用 ============
# 16 字节随机串,每次部署必须唯一(可用 openssl rand -base64 12 生成)
SECRET_KEY=5ku+ABoBFSidF23i
# 服务栈网段,必须与编排文件里的网桥子网一致
SUBNET=10.89.56.0/24
# 主邮件域(生成 postmaster 等技术地址、退信发件人)
DOMAIN=example.com
# 对外主机名,逗号分隔,第一个为主主机名(证书 CN 也要覆盖它)
HOSTNAMES=mail.example.com
# postmaster 的本地部分
POSTMASTER=admin
# TLS 策略:letsencrypt/mail-letsencrypt/cert/mail/notls
TLS_FLAVOR=cert
# 认证限速:按 IP 段 / 按账号
AUTH_RATELIMIT_IP=60/hour
AUTH_RATELIMIT_USER=100/day
# 关闭匿名统计上报
DISABLE_STATISTICS=True
# ============ 可选功能 ============
ADMIN=true
WEBMAIL=none
API=true
WEBDAV=none
ANTIVIRUS=none
SCAN_MACROS=false
# ============ 邮件设置 ============
MESSAGE_SIZE_LIMIT=50000000
RELAYNETS=
RELAYHOST=
FETCHMAIL_ENABLED=False
FETCHMAIL_DELAY=600
RECIPIENT_DELIMITER=+
DMARC_RUA=admin
DMARC_RUF=admin
WELCOME=false
WELCOME_SUBJECT=Welcome to your new email account
WELCOME_BODY=Welcome to your new email account, if you can read this, then it is configured properly!
COMPRESSION=
COMPRESSION_LEVEL=6
FULL_TEXT_SEARCH=en
# front 对外启用的邮件端口清单;默认 25,80,443,465,993,995,4190,
# Submission(587)/IMAP(143)/POP3(110) 必须显式加入
PORTS=25,80,443,465,587,993,995,4190
# ============ Web 设置 ============
WEBROOT_REDIRECT=/admin
WEB_ADMIN=/admin
WEB_WEBMAIL=/webmail
WEB_API=/api
SITENAME=Mailu
WEBSITE=https://mail.example.com
# ============ 高级设置 ============
COMPOSE_PROJECT_NAME=mailu-2024.06.65-stack
CREDENTIAL_ROUNDS=12
REAL_IP_HEADER=
REAL_IP_FROM=
REJECT_UNLISTED_RECIPIENT=yes
LOG_LEVEL=INFO
TZ=Asia/Shanghai
DEFAULT_SPAM_THRESHOLD=80
# RESTful API 令牌(最短 3 字符),API=true 时必配
API_TOKEN=q1w2e3r4
# 首启自动创建管理员:create/ifmissing/update 三种模式
INITIAL_ADMIN_ACCOUNT=admin
INITIAL_ADMIN_DOMAIN=example.com
INITIAL_ADMIN_PW=q1w2e3r4
INITIAL_ADMIN_MODE=ifmissing
FULL_TEXT_SEARCH_ATTACHMENTS=false
关键项解读:
| 参数 | 取值 | 作用与影响 |
|---|---|---|
SECRET_KEY | 16 字节随机串 | 会话 Cookie 签名等关键加密用途,更换后所有登录态失效 |
SUBNET | 10.89.56.0/24 | 必须等于编排文件网桥子网,否则各角色互访异常 |
TLS_FLAVOR | cert | 从 /certs 读自带证书;若公网想自动签发改 letsencrypt |
PORTS | 含 587 | 不加 587 则 Submission 端口根本不监听(默认值不含,本文实测踩坑,见第十章) |
INITIAL_ADMIN_MODE | ifmissing | 已存在则跳过(重启安全);create 重启会报错,官方建议 ifmissing/update |
API_TOKEN | q1w2e3r4 | 启用 RESTful API 的 Bearer 令牌 |
TZ | Asia/Shanghai | 服务栈内时区,日志时间戳随之 |
4.3 编写 docker-compose.yml
在 /opt/data/quzuo/tmp/mailu/compose/docker-compose.yml 写入以下内容(在官方 setup 向导生成模板基础上做了三处本地化:显式项目名、网段 10.89.56.0/24、front 端口全部改为映射到 8128–8132 段):
name: mailu-2024.06.65-stack
services:
redis:
image: redis:alpine
restart: always
volumes:
- "/opt/data/quzuo/tmp/mailu/data/redis:/data"
depends_on:
- resolver
dns:
- 10.89.56.254
front:
image: ghcr.io/mailu/nginx:2024.06.65
restart: always
env_file: mailu.env
logging:
driver: journald
options:
tag: mailu-front
ports:
- "8128:25"
- "8129:465"
- "8130:587"
- "8131:993"
- "8132:443"
networks:
- default
volumes:
- "/opt/data/quzuo/tmp/mailu/data/certs:/certs"
- "/opt/data/quzuo/tmp/mailu/data/overrides/nginx:/overrides:ro"
depends_on:
- resolver
dns:
- 10.89.56.254
resolver:
image: ghcr.io/mailu/unbound:2024.06.65
env_file: mailu.env
logging:
driver: journald
options:
tag: mailu-resolver
restart: always
networks:
default:
ipv4_address: 10.89.56.254
admin:
image: ghcr.io/mailu/admin:2024.06.65
restart: always
env_file: mailu.env
logging:
driver: journald
options:
tag: mailu-admin
volumes:
- "/opt/data/quzuo/tmp/mailu/data/main:/data"
- "/opt/data/quzuo/tmp/mailu/data/dkim:/dkim"
depends_on:
- redis
- resolver
dns:
- 10.89.56.254
imap:
image: ghcr.io/mailu/dovecot:2024.06.65
restart: always
env_file: mailu.env
logging:
driver: journald
options:
tag: mailu-imap
volumes:
- "/opt/data/quzuo/tmp/mailu/data/mail:/mail"
- "/opt/data/quzuo/tmp/mailu/data/overrides/dovecot:/overrides:ro"
networks:
- default
depends_on:
- front
- resolver
dns:
- 10.89.56.254
smtp:
image: ghcr.io/mailu/postfix:2024.06.65
restart: always
env_file: mailu.env
logging:
driver: journald
options:
tag: mailu-smtp
volumes:
- "/opt/data/quzuo/tmp/mailu/data/mailqueue:/queue"
- "/opt/data/quzuo/tmp/mailu/data/overrides/postfix:/overrides:ro"
depends_on:
- front
- resolver
dns:
- 10.89.56.254
antispam:
image: ghcr.io/mailu/rspamd:2024.06.65
hostname: antispam
restart: always
env_file: mailu.env
logging:
driver: journald
options:
tag: mailu-antispam
networks:
- default
volumes:
- "/opt/data/quzuo/tmp/mailu/data/filter:/var/lib/rspamd"
- "/opt/data/quzuo/tmp/mailu/data/overrides/rspamd:/overrides:ro"
depends_on:
- front
- redis
- resolver
dns:
- 10.89.56.254
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 10.89.56.0/24
配置要点逐项说明:
name: mailu-2024.06.65-stack:显式服务栈项目名(版本号中的点号会被 Docker 规范化,实际运行时项目内资源名显示为mailu-20240665-stack-*)。共享主机上多个编排项目并存时,显式项目名可避免同名项目互相干扰。resolver固定 IP10.89.56.254:Mailu 强制所有角色用内建 Unbound 做 DNSSEC 解析,dns: 10.89.56.254把各角色的 DNS 指向它;该 IP 必须在SUBNET网段内。- 端口映射
8128-8132:官方模板把 10 个端口直接绑宿主,此处改为 5 条映射(选择理由与省略清单见第八章)。 env_file: mailu.env:除 resolver 外每个角色都注入同一份环境配置;resolver 也读mailu.env(其转发行为受配置影响)。- 数据卷:全部 bind mount 到
/opt/data/quzuo/tmp/mailu/data/子目录,映射关系见第五章。
4.4 启动服务栈
$ cd /opt/data/quzuo/tmp/mailu/compose
$ docker compose up -d
Container mailu-20240665-stack-resolver-1 Starting
Container mailu-20240665-stack-resolver-1 Started
Container mailu-20240665-stack-front-1 Starting
Container mailu-20240665-stack-redis-1 Starting
Container mailu-20240665-stack-front-1 Started
Container mailu-20240665-stack-imap-1 Starting
Container mailu-20240665-stack-smtp-1 Starting
Container mailu-20240665-stack-redis-1 Started
Container mailu-20240665-stack-antispam-1 Starting
Container mailu-20240665-stack-admin-1 Starting
Container mailu-20240665-stack-imap-1 Started
Container mailu-20240665-stack-smtp-1 Started
Container mailu-20240665-stack-antispam-1 Started
Container mailu-20240665-stack-admin-1 Started
💡 首次执行会先自动拉取 7 个服务单元(约 2.5GB),拉取完成后逐个启动。启动顺序由依赖关系决定:resolver 最先,admin 最后。
等待半分钟左右,确认七服务全部就绪(admin/imap/resolver 需完成健康检查,从 health: starting 变为 healthy):
$ docker compose ps --format 'table {{.Service}}\t{{.Status}}'
SERVICE STATUS
admin Up 28 seconds (health: starting)
antispam Up 29 seconds (healthy)
front Up 29 seconds (healthy)
imap Up 29 seconds (health: starting)
redis Up 29 seconds
resolver Up 29 seconds (health: starting)
smtp Up 29 seconds (healthy)
⚠️ 注意:admin 的健康检查内含 DNSSEC 强制自检(解析
example.org验证 AD 标志位),若内建 resolver 异常,admin 将拒绝启动并在日志里输出 "Your DNS resolver isn't doing DNSSEC validation"。这是官方反 DNS 欺骗的刻意设计,不要试图绕过,排查 resolver 即可。
4.5 验证初始管理员自动创建
admin 首启会按 INITIAL_ADMIN_* 变量创建全局管理员(domain 也会随之自动创建):
$ docker compose logs admin 2>&1 | grep -i 'creating initial'
admin-1 | INFO:root:Creating initial admin account admin@example.com with mode ifmissing
用导出命令核对账号与域已落库(导出命令详解见本专栏《Mailu 2024.06.65 常用 CLI 命令详解》):
$ docker compose exec -T admin flask mailu config-export user.email user.global_admin
user:
- email: admin@example.com
global_admin: true
- email: user1@example.com
global_admin: false
(user1 为后续步骤创建的测试账号;全新部署此处只有 admin 一条。)
4.6 验证 Web 管理台
$ curl -sk -o /dev/null -w '%{http_code} -> %{redirect_url}\n' https://127.0.0.1:8132/
308 -> https://127.0.0.1:8132/admin/?homepage
$ curl -skL -o /dev/null -w 'admin UI final: %{http_code} %{url_effective}\n' https://127.0.0.1:8132/admin/
admin UI final: 200 https://127.0.0.1:8132/sso/login?url=https://127.0.0.1:8132/admin/
根路径 308 跳转到管理台,未登录最终落在统一登录页 /sso/login(HTTP 200)。浏览器访问 https://mail.example.com:8132/admin/(或 https://192.168.146.128:8132/admin/),用 admin@example.com / q1w2e3r4 登录即可进入管理界面。自签证书会触发浏览器告警,属预期行为(换正式证书即消除)。
4.7 创建邮件用户
用官方管理命令行创建普通用户 user1@example.com(成功时静默无输出):
$ docker compose exec -T admin flask mailu user user1 example.com 'q1w2e3r4'
$ docker compose exec -T admin flask mailu config-export user.email user.enabled
user:
- email: admin@example.com
enabled: true
- email: user1@example.com
enabled: true
4.8 验证宿主端口监听
$ ss -ltn | grep -E ':(8128|8129|8130|8131|8132)\b'
LISTEN 0 4096 0.0.0.0:8132 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:8128 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:8129 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:8130 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:8131 0.0.0.0:*
五个宿主端口全部就绪。
4.9 SMTP/IMAP 协议级实测
① 465 SMTPS 握手(服务器本机,-quiet 抑制证书告警噪音):
$ (printf 'EHLO test.example.org\r\nQUIT\r\n'; sleep 2) | \
timeout 8 openssl s_client -connect 127.0.0.1:8129 -quiet 2>&1 | head -12
Can't use SSL_get_servername
depth=0 CN = mail.example.com, O = Mailu Demo
verify error:num=18:self-signed certificate
verify return:1
depth=0 CN = mail.example.com, O = Mailu Demo
verify return:1
220 mail.example.com Dovecot ready.
250-mail.example.com
250-8BITMIME
250-AUTH PLAIN LOGIN
250-BURL imap
250-CHUNKING
TLS 握手成功,证书即第 4.1 步的自签证书(CN=mail.example.com),对端自称 mail.example.com Dovecot ready。
② 25 端口入站投递(客户端机向 user1@example.com 投一封测试邮件):
$ swaks --from admin@example.com --to user1@example.com \
--server mail.example.com:8128 --h-Subject 'B2 inbound test'
<- 250-ENHANCEDSTATUSCODES
<- 250-8BITMIME
<- 250-DSN
<- 250 STARTTLS
-> MAIL FROM:<admin@example.com>
<- 250 2.0.0 OK
-> RCPT TO:<user1@example.com>
<- 250 2.1.5 Ok
-> DATA
<- 354 End data with <CR><LF>.<CR><LF>
-> Date: Wed, 21 Oct 2026 00:43:04 +0000
-> To: user1@example.com
-> From: admin@example.com
-> Subject: B2 inbound test
-> Message-Id: <20261021004304.000416@64e0fe571313>
-> X-Mailer: swaks v20201014.0 jetmore.org/john/code/swaks/
->
-> This is a test mailing
->
->
-> .
<- 250 2.0.0 Ok: queued as 21BD58C227B
-> QUIT
<- 221 2.0.0 Bye
=== Connection closed with remote host.
入站邮件被队列接受(queued as 21BD58C227B)。
③ 587 认证提交 + STARTTLS(user1 向 admin 发信,验证认证与加密通道):
$ swaks --from user1@example.com --to admin@example.com \
--server mail.example.com:8130 \
--auth PLAIN --auth-user user1@example.com --auth-password q1w2e3r4 --tls
<~ 220 mail.example.com Dovecot ready.
<~ 250-mail.example.com
<~ 250-8BITMIME
<~ 250-SIZE 50000000
<~ 250-STARTTLS
<~ 220 2.0.0 Begin TLS negotiation now.
(TLS 协商成功,后续为密文会话)
~> RCPT TO:<admin@example.com>
<~ 250 2.1.5 Ok
~> DATA
<~ 354 End data with <CR><LF>.<CR><LF>
~> .
<~ 250 2.0.0 Ok: queued as E012B8C22CE
~> QUIT
<~ 221 2.0.0 Bye
注意回显 250-SIZE 50000000 正是 MESSAGE_SIZE_LIMIT 生效值;--auth PLAIN 在 STARTTLS 之后再发起,明文口令不出网。
④ 993 IMAPS 登录读信(服务器本机验证 user1 收到了第②步的邮件):
$ (printf 'a1 LOGIN user1@example.com q1w2e3r4\r\na2 SELECT INBOX\r\na3 FETCH 1 (BODY[HEADER.FIELDS (FROM SUBJECT)])\r\na4 LOGOUT\r\n'; sleep 3) | \
timeout 10 openssl s_client -connect 127.0.0.1:8131 -quiet 2>/dev/null
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
a1 OK [CAPABILITY IMAP4rev1 ... QUOTA] Logged in
* 1 EXISTS
* 1 RECENT
* OK [UNSEEN 1] First unseen.
a2 OK [READ-WRITE] Select completed (0.001 + 0.000 secs).
* 1 FETCH (FLAGS (\Seen \Recent) BODY[HEADER.FIELDS (FROM SUBJECT)] {53}
From: admin@example.com
Subject: B2 inbound test
)
1 EXISTS + FETCH 命中 From: admin@example.com / Subject: B2 inbound test——25 端口入站 → Dovecot 落箱 → 993 可读,收信链路闭环。再验证 admin 的收件箱(第③步那封):
$ (printf 'b1 LOGIN admin@example.com q1w2e3r4\r\nb2 SELECT INBOX\r\nb3 FETCH 1 (BODY[HEADER.FIELDS (FROM SUBJECT)])\r\nb4 LOGOUT\r\n'; sleep 3) | \
timeout 10 openssl s_client -connect 127.0.0.1:8131 -quiet 2>/dev/null | grep -E '^(b[0-9]|From:|Subject:|\* 1)'
b1 OK [CAPABILITY IMAP4rev1 ... QUOTA] Logged in
* 1 FETCH (FLAGS (\Seen \Recent) BODY[HEADER.FIELDS (FROM SUBJECT)] {74}
From: user1@example.com
Subject: test Wed, 21 Oct 2026 00:44:20 +0000
b3 OK Fetch completed (0.001 + 0.000 secs).
b4 OK Logout completed (0.001 + 0.000 secs.)
双向收发全部闭环。
五、数据持久化说明
全栈数据用 bind mount 落在 /opt/data/quzuo/tmp/mailu/data/,重启/重建服务不丢:
| 宿主目录 | 服务内挂载点 | 内容 |
|---|---|---|
data/main | admin 的 /data | 管理数据库(SQLite)、DKIM 密钥索引、webmail 数据 |
data/dkim | admin 的 /dkim | DKIM 签名私钥 |
data/mail | imap 的 /mail | 全部用户 Maildir 邮件本体 |
data/mailqueue | smtp 的 /queue | Postfix 出站队列 |
data/filter | antispam 的 /var/lib/rspamd | Rspamd 学习数据与统计 |
data/certs | front 的 /certs | TLS 证书/私钥(cert.pem/key.pem) |
data/redis | redis 的 /data | 缓存与限速计数 |
data/overrides/* | 各服务 /overrides(只读) | 官方预留的配置覆盖目录(见第七章) |
备份即打包:tar czf mailu-backup.tar.gz /opt/data/quzuo/tmp/mailu/data/(最好先 docker compose stop 再备,保证 Maildir 与 SQLite 一致性)。
六、环境变量表
mailu.env 全部变量以官方配置参考为准,上表之外的常用运维向变量:
| 变量 | 默认值 | 说明 |
|---|---|---|
PORTS | 25,80,443,465,993,995,4190 | front 启用的协议端口清单;25/465/993 不可禁用 |
MESSAGE_RATELIMIT | 200/day | 单用户日发信上限(防被盗号后狂发) |
DEFAULT_QUOTA | 未设置 | 域未配置配额时的新用户默认配额 |
ANTIVIRUS_ACTION | discard | 查出病毒是丢弃(reject 则 SMTP 阶段拒收) |
SESSION_TIMEOUT | 3600 | 管理台会话空闲超时(秒) |
PERMANENT_SESSION_LIFETIME | 108000 | 会话最长存活(秒) |
LETSENCRYPT_SHORTCHAIN | False | Let's Encrypt 证书链兼容老安卓开关 |
SQLALCHEMY_DATABASE_URI | sqlite:////data/main.db | 管理库连接(官方不建议换库) |
逐参数深度解析见本专栏《Mailu 2024.06.65 配置参数详解》。
七、配置文件挂载
各服务的 /overrides 目录(只读挂载)是官方预留的配置覆盖通道:在宿主 data/overrides/<服务>/ 放同名配置文件,即可覆盖服务内默认配置而无需改服务单元内容。常用场景:
overrides/postfix/main.cf:追加 Postfix 自定义主配置(如额外 smtpd 规则);overrides/dovecot/conf.d/*.conf:Dovecot 自定义(如邮箱目录结构微调);overrides/nginx/*.conf:front 的 Nginx 片段(如自定义 location);overrides/rspamd/*.conf:Rspamd 模块级覆盖。
改动 overrides 后需要重建对应服务(docker compose up -d --force-recreate <服务名>)才能生效。
八、端口映射与省略说明
官方 setup 向导生成的编排把 front 的 10 个端口全部绑到宿主:25, 80, 443, 465, 110, 995, 143, 993, 4190。本部署运行在共享主机上,经典邮件端口不可直接绑宿主,故改为端口映射形态,从预分配的 8128–8132 段取 5 个端口:
| 宿主端口 | 服务内端口 | 协议 | 是否映射 | 说明 |
|---|---|---|---|---|
| 8128 | 25 | SMTP | ✔ | 入站邮件主通道 |
| 8129 | 465 | SMTPS(隐式 TLS) | ✔ | 客户端加密提交 |
| 8130 | 587 | Submission(STARTTLS) | ✔ | 客户端提交标准口(需 PORTS 显式含 587) |
| 8131 | 993 | IMAPS(隐式 TLS) | ✔ | 邮件客户端读取 |
| 8132 | 443 | HTTPS | ✔ | 管理台/API 入口 |
| — | 80 | HTTP | ✘ 省略 | 管理台走 443 已够用;80 主要服务 Let's Encrypt HTTP-01 校验(letsencrypt 模式才需要) |
| — | 110/995 | POP3/POP3S | ✘ 省略 | 现代客户端基本走 IMAP;需要时把映射加回即可 |
| — | 143 | IMAP(明文+STARTTLS) | ✘ 省略 | 已映射 993(隐式 TLS),更安全 |
| — | 4190 | Managesieve | ✘ 省略 | 桌面客户端管理 Sieve 过滤规则用,管理台可代管 |
取舍原则:保留"能完整闭环收发+管理"的最小集合。客户端侧配置示例(以 Thunderbird 类客户端为参照):收件服务器 mail.example.com:8131(IMAPS),发件服务器 mail.example.com:8130(STARTTLS)或 mail.example.com:8129(SSL/TLS)。公网生产部署建议把 25/465/587/993/443 直接绑宿主(官方默认形态),并保留 80 供证书自动续期。
九、常用运维命令
以下命令均在部署目录 /opt/data/quzuo/tmp/mailu/compose/ 下执行:
# 查看服务状态
$ docker compose ps
# 查看某服务日志(journald tag 均已打上 mailu-<角色> 标签)
$ docker compose logs --tail 50 admin
# 跟随 antispam 日志
$ docker compose logs -f antispam
# 手动拉取新版并滚动升级(锁定版部署时,把两份文件里的 2024.06.65 改成新锁定版号)
$ docker compose pull && docker compose up -d
# 重启单个服务
$ docker compose restart front
# 重建某服务(改了 overrides 或环境变量后)
$ docker compose up -d --force-recreate admin
# 停止 / 彻底移除(数据在 bind mount,down 不删数据)
$ docker compose down
升级流程小结:改镜像 tag → docker compose pull → docker compose up -d(自动滚动重建受影响服务);回滚同理把 tag 改回旧锁定版即可。
十、常见问题
Q1:587 端口连接被拒(Connection refused),25/465 却正常?
实测踩坑记录:部署完成后从客户端提交报错:
*** Error connecting to mail.example.com:8130:
*** IO::Socket::INET6: connect: Connection refused
定位:ss -ltn 宿主侧 8130 明明在监听,但服务内 587 没开。原因:2024.06 的 PORTS 默认值是 25,80,443,465,993,995,4190,不含 587/143/110(官方配置参考原话:"If you need to re-enable IMAP, POP3 and Submission, you can append '110,143,587' to that list")。解决:在 mailu.env 加 PORTS=25,80,443,465,587,993,995,4190,然后 docker compose up -d 重建服务,再测即通。
Q2:admin 服务反复重启,日志报 DNS resolver 相关 critical?
admin 启动自检强制要求 DNSSEC 校验通过(解析 example.org 检查 AD 标志)。若宿主防火墙拦了出站 DNS 或 resolver 未就绪,admin 会一直报 "Your DNS resolver isn't doing DNSSEC validation" 并拒绝启动。排查 docker compose logs resolver,确认出站 53 端口(TCP/UDP)可达;本实验环境 resolver 就绪后 admin 即转 healthy。
Q3:浏览器/openssl 报 self-signed certificate 告警?
TLS_FLAVOR=cert 只负责"使用你提供的证书",不校验其来源;自签证书必然触发告警。生产环境换成 Let's Encrypt(TLS_FLAVOR=letsencrypt,需要 80/443 对公网开放)或正规商业证书(保持 cert 模式、替换 cert.pem/key.pem,front 会自动热加载——内建 certwatcher 监听证书文件变化)。
Q4:改了 mailu.env 不生效?
env_file 注入发生在服务重建时,改完必须 docker compose up -d(会自动重建受影响服务)或 up -d --force-recreate。仅 restart 不会重新读取环境文件。
Q5:命令行建的域没有 DKIM 密钥?
flask mailu domain / flask mailu admin 建域时不生成 DKIM 密钥(管理台 UI 建域才会生成)。补救:管理台进入该域点"重新生成密钥",或调 REST API POST /api/v1/domain/<域名>/dkim。
十一、参考链接
- Mailu 官方文档(2024.06):https://mailu.io/2024.06/
- Docker Compose 部署要求:https://mailu.io/2024.06/compose/requirements.html
- 官方 setup 向导:https://setup.mailu.io/
- 配置参考:https://mailu.io/2024.06/configuration.html
- 管理命令行:https://mailu.io/2024.06/cli.html
- GitHub 仓库:https://github.com/Mailu/Mailu
- 本站相关文章:《Mailu 2024.06.65 介绍与选型指南》《Mailcow 2026-09a 介绍与选型指南》