Mailcow:开源容器化邮件服务器实战,从DNS到投递的全链路避坑
2026/9/25 1:03:22 网站建设 项目流程

简介:Mailcow是一个基于Docker容器化技术构建的开源邮件服务器解决方案,主要面向需要自建高效、安全邮件系统的运维人员与中小企业IT管理员。它整合了SMTP、IMAP、POP3、Webmail等服务,并内置反垃圾、反病毒、DKIM、DMARC、SPF等安全功能,有效解决传统邮件服务器部署复杂、维护困难的问题。这套资源包为Mailcow项目的完整文件,共2000个文件,以1515个PHP核心代码文件为主,辅以Markdown文档、JSON配置文件、Shell脚本、XML配置、Dockerfile等,这些文件分别承担了邮件处理逻辑、服务编排、参数自定义及自动化运维等角色,可帮助读者深入理解整体架构与组件协作。压缩包大小约10.98MB,目录结构清晰,特别适合学习Docker Compose编排、多服务联动、邮件安全策略落地等场景。已有200人学习下载,对希望快速掌握自建邮件服务器技术的读者具有实用参考价值。

1. 自建邮箱到底图什么:Mailcow 想解决的那几件事

公司邮箱挂在第三方平台上,管理员后台看不到邮件流转日志,附件归档全部依赖对方良心,团队邮件数据说不上被谁碰过——这种状态下想换一套能自主控制、功能又不缩水的开源邮件服务器,Mailcow 是绕不开的名字。它不是某个单一软件,而是一整套基于 Docker 编排的开源邮件服务器解决方案,把 Postfix、Dovecot、SoGo、Rspamd、ClamAV 这些组件拼成一个开箱即用的全家桶,覆盖收发信、Webmail、反垃圾、杀毒、证书续签全部环节。适合小团队自建、个人域名邮箱、或者想摆脱第三方平台束缚的企业 IT 负责人。硬件要求不高,配置门槛主要体现在 DNS 和网络端口上,这也是绝大多数人卡住的地方。

2. 拆开 Mailcow 看看:Postfix、Dovecot、Rspamd 与容器化的取舍

2.1 从信件进门到出门:一条完整的投递链路

要理解 Mailcow 为什么好用,得先看一封邮件在它内部是怎么流转的。外部发来的信先落在 Postfix 上,Postfix 是邮件传输代理,负责跟外部邮件系统说 SMTP 协议的话;信收下来之后,会依次经过 Rspamd 做反垃圾评分、ClamAV 查病毒,这两关过了才交给 Dovecot 的 LDA 投递进程写进用户邮箱目录。发信的方向反过来:客户端通过 587 端口提交邮件给 Postfix,Postfix 对邮件做 DKIM 签名,然后查询对方域名的 MX 记录,把信投给对方的邮件服务器。

这套链路里每个容器各管一段,下表把组件职责列清楚。

容器/组件职责对外端口
PostfixSMTP 收发信、DKIM 签名25 / 587
DovecotIMAP/POP3 存取、用户认证、本地投递143 / 993 / 110 / 995
SoGoWebmail、通讯录、日历、会议邀请由前端入口转发
Rspamd反垃圾评分、SPF/DKIM 校验内部端口
ClamAV病毒扫描内部端口
MySQL/MariaDB域名、邮箱账号、别名等元数据内部端口
Redis会话缓存、Rspamd 统计、SoGo 状态内部端口
Acme自动申请与续期 TLS 证书内部端口

组件分离的价值在于改动面是可控的:调反垃圾策略时你碰 Rspamd 就行,没必要动 MTA;Webmail 界面换皮肤也不会影响邮件投递主链路。如果你之前折腾过裸装邮件服务器,应该感受过那种牵一发动全身的疼——改个 amavis 配置把整个服务搞挂的事我干过不止一次,Mailcow 这种容器化的拼装方式至少让每次改动的爆炸半径变小了。

2.2 为什么不是直接在服务器装一套邮件软件

裸装一个邮件系统难点不在装包,而在依赖冲突和升级包袱。Rspamd 和 ClamAV 对 libc 和编译器版本敏感,CentOS 7 上编译 ClamAV 时踩了一下午的坑,换了台 Debian 又遇到 Redis 版本不对导致 SoGo 会话写入失败。用 Docker Compose 编排之后,这些依赖问题都被镜像隔离掉了,你维护的是 compose 文件和几个数据卷,而不是一台越滚越大的实体服务器。

Mailcow 的配置体系分两层:一层是根目录的mailcow.conf,它保存域名、时区、数据库密码、证书策略这类顶层参数;另一层是docker-compose.yml,由配置脚本生成,编排各容器。日常运维时不要去手工改docker-compose.yml里的容器参数,临时想加内存限制或改日志轮转,正确做法是写一个docker-compose.override.yml,compose 启动时会自动合并加载。这个习惯养成以后,升级 Mailcow 版本时你的自定义项不会因为重新生成配置而丢失。

3. 从空白服务器到第一封邮件:部署步骤与 mailcow.conf 关键参数

3.1 前置条件:域名、DNS 与机器资源

部署前先把 DNS 捋清楚,这是整个项目里最容易翻车的一步。你需要一个域名,最好把子域专门留给邮件用,比如mail.example.com。先创建 A 记录把mail指向服务器公网 IP,然后建 MX 记录指向mail.example.com,再给mail.example.com做 PTR 反向解析——PTR 是很多云厂商控制台里单独提供的功能,没有 PTR 的话,对方服务器做反向 DNS 校验时会直接降级你的信誉分。

资源方面,2 核 4G 内存是起步线,ClamAV 扫描时内存很容易冲到 2G 以上,1G 内存的机器不是跑不起来,而是 Swap 会被打满,整机响应变慢,届时你分不清是邮件服务的问题还是系统资源不够。磁盘给 50G 起步,邮件附件增长比你想得快。

部署前需要确认的 DNS 记录如下:

记录类型主机记录记录值说明
Amail服务器公网 IP必填
MX@mail.example.com优先级 10
PTR公网 IPmail.example.com多数云需单独申请
TXTmailv=spf1...SPF 记录,见第 4 章

3.2 生成配置:mailcow.conf 里值得手动确认的参数

服务器上装好 Docker 和 Docker Compose 插件之后,把项目拉下来,执行配置生成脚本,整个过程是向导式的。脚本的第一个问题会让你填主机名,也就是mail.example.com这种格式,这里填错后面所有证书申请都会跟着错。

# 拉取项目源码 git clone https://github.com/mailcow/mailcow-dockerized cd mailcow-dockerized # 生成配置文件,过程中会交互式询问主机名 ./generate_config.sh

脚本跑完会在项目根目录生成mailcow.conf,下一步是手动改几个关键参数。不要跳过这步直接启动容器,默认时区是 UTC,国内服务器不改成 Asia/Shanghai,后面日志时间、邮件 Date 头全都差 8 小时,排查问题的时候非常折磨。

# 编辑 mailcow.conf,按需修改以下参数 # MAILCOW_HOSTNAME=mail.example.com # 与 generate_config.sh 填写的保持一致 # TZ=Asia/Shanghai # 时区改为 Asia/Shanghai # HTTP_PORT=80 # 默认即可,如需改端口注意与防火墙同步 # HTTPS_PORT=443 # 默认即可 # SKIP_LETS_ENCRYPT=n # 有自有证书可设 y,否则保持 n

MAILCOW_HOSTNAME决定证书申请与生成 DKIM 记录时用的域名,TZ影响全部容器内进程的时区,SKIP_LETS_ENCRYPT在还没有正式域名或申请失败时可以临时设成y,邮件功能不受影响,只是 Web UI 会提示证书不可信。改完保存,执行下面这段命令拉取镜像并启动全套容器:

# 拉取镜像并后台启动所有容器 docker compose pull docker compose up -d

首次启动时要拉十几个镜像,耗时取决于网络,建议提前给 Docker 配置镜像加速。等容器全部进入 running 状态后,浏览器访问https://服务器IP,能打开登录页说明核心服务起来了。

3.3 从 Web UI 到第一封外发邮件

Mailcow 的初始管理员账号固定是admin,初始密码不是写在文档里的,而是随机生成后保存在mailcow.conf里。查密码执行下面这段命令:

# 查看 admin 账号初始密码 grep -i "admin_pass" mailcow.conf

第一次登录后会强制改密码。进入后台后第一件事是创建域名:在「配置 → 域名」里点新增,填写你的域名并选择「添加域名并配置 MX」。Mailcow 会自动生成对应的 MX、SPF 记录列表,照着填到 DNS 服务商那里即可,这一步相当于把后台的元数据建好。

域名创建后,到「配置 → 邮箱」新增一个邮箱账号,设置好密码。此时用任意邮件客户端配置 IMAP/SMTP——服务器地址填mail.example.com,IMAP 端口 993 带 SSL、SMTP 端口 587 选 STARTTLS——就能收发信了。第一封外发邮件建议发给一个 Gmail 或 Outlook 邮箱,同时做好垃圾箱的心理准备,域名刚启用时信誉为零,被拦的概率不低。具体怎么把信誉做起来,看第 4 章和第 6 章。

4. 上线别急着发信:SPF/DKIM/DMARC 三件套与备份恢复

4.1 让外面的世界信任你:SPF、DKIM、DMARC 配置

域名能收发信和域名不被判垃圾是两回事。SPF 声明哪些 IP 被授权发送该域名的邮件,DKIM 给每封邮件做数字签名,DMARC 告诉对方服务器验证失败时怎么处理,三者缺一个都会被主流邮箱降级。

Mailcow 后台的 DKIM 配置在「配置 → 域名 → 域名管理」里,选中域名点 DKIM 按钮,选择 2048 位密钥生成。生成后页面会显示一条 TXT 记录,主机记录 값 形如dkim._domainkey,记录值形如v=DKIM1; k=rsa; p=MIGf...,把这串东西原样复制到 DNS 服务商。

SPF 和 DMARC 我用下面这些记录,直接照着填即可。

记录类型主机记录
SPF(TXT)@v=spf1 mx ip4:服务器公网IP -all
DMARC(TXT)_dmarcv=DMARC1; p=quarantine; rua=mailto:admin@example.com

SPF 里mx表示允许 MX 记录指向的服务器发送,ip4:后面写死公网 IP,-all表示除此之外都拒绝,这是比较严格的策略,如果后续有第三方邮件营销平台代发,要额外加include:规则。DMARC 的p=quarantine表示验证失败进垃圾箱,等跑一段时间确认没有误拦,再改成p=reject,这样域名被伪造的概率会低很多。

DNS 生效后建议做一次验证,确保解析结果正确:

# 验证 MX、SPF、DKIM 解析是否已生效 dig MX mail.example.com dig TXT mail.example.com dig TXT dkim._domainkey.mail.example.com

注意 DKIM 的查询域名是dkim._domainkey.mail.example.com,不是裸域。DNS 传播一般几分钟到几小时不等,没生效时别急着重启容器。

4.2 备份、恢复与升级:每台邮件服务器都需要三份后悔药

邮件数据丢了没有后悔药这个概念,所以备份要从上线的第一天就做。Mailcow 自带的备份脚本在helper-scripts/backup.sh,它打的不是文件快照,而是把 MySQL 数据、邮箱存储、Redis 数据这些 Docker 卷打包,这是最省事的备份方式。建议配合 crontab 每晚跑一次,备份文件至少保留 7 天。

# 一键备份全部数据卷到 backup 目录并保留历史版本 ./helper-scripts/backup.sh -c /opt/backup

-c参数指定备份输出目录,脚本会按日期生成子目录。恢复时用-r参数指明备份目录,脚本会把卷里的数据原样放回去。恢复前先docker compose down停掉全部容器,避免数据写入冲突。

升级是很多人不敢碰的一步,其实 Mailcow 的更新脚本设计得已经把风险压到最低了。执行./update.sh前先把mailcow.conf里关键参数手抄一份,同时确认备份目录空间充足。更新脚本会自动处理镜像拉取、数据库迁移和配置合并,自定义修改要留意输出日志里有没有提示覆盖,这也就是我前面强调用docker-compose.override.yml存自定义项的原因——默认配置覆盖不了它,自然也就不会丢。

5. 邮件服务器避坑手册:25 端口、OOM 与 DKIM 不生效

5.1 出站投递的三道坎

下面这几类坑是我在多个生产环境里真实遇到的,每一条都按「现象 → 原因 → 解决」拆开讲,建议照着排查一遍。

第一道坎:25 端口出不去,外发邮件全部超时。
现象:邮件发送队列越积越多,Postfix 日志里全是connect to xxx[IP]:25: Connection timed out
原因:国内大多云厂商默认封禁 25 端口出方向,阿里云、腾讯云都要单独提工单解封,而且解封通常只对已备案域名开放。
解决:先用nc -vz 对方邮件服务器IP 25测通不通,确认被封就去云厂商控制台提交工单,说明用途并承诺不发送垃圾邮件。要留意的是,有些厂商解封的只是入方向 25,出方向还要再确认一次,否则你的服务器能收信但不能发信,看上去像是配置问题,实际是网络策略问题。

第二道坎:SPF 记录本身带坑。
现象:发给 Gmail 的邮件被拒,退信内容提示SPF_PASSDKIM_FAIL,或者干脆出现在收件人垃圾箱。
原因:SPF 的 TXT 记录只能有一条,如果同一条记录里重复出现多个ip4:或者-all+all同时存在,对方解析时按严格模式判断就直接失败。
解决:用 SPF 记录生成器重新生成,把mxip4:include:按顺序排好,最后以-all收尾。改完用dig TXT mail.example.com确认记录完整。

第三道坎:改了 DKIM 记录却不生效。
现象:DNS 上 DKIM 记录已经更新,但邮件头里的Authentication-Results仍是旧签名。
原因:Rspamd 的 DKIM 密钥缓存在 Redis 里,DNS 更新不会自动刷新缓存,老签名还在被重复使用。
解决:改完 DKIM 后重启 Rspamd 容器并强制清 Redis 缓存。具体命令见下:

# 重启 Rspamd 容器,强制重新加载密钥 docker compose restart rspamd # 清理 Redis 中与 DKIM 相关的缓存键 docker compose exec redis redis-cli DEL "$(docker compose exec redis redis-cli KEYS '*dkim*' | tr '\n' ' ')"

5.2 资源与配置的坑

第四道坎:ClamAV 内存冲到 3G,容器被 OOM 杀掉。
现象:运行一段时间后docker compose ps里 clamav 容器显示 Restarting,dmesg里出现Out of memory: Kill process,日志尾部全是 ClamAV 的扫描记录。
原因:ClamAV 默认线程数偏多,邮件附件多时同时解压扫描,内存占用直接拉满,1G/2G 内存的机器很容易触发内核 OOM。
解决:在项目根目录写docker-compose.override.yml,给 clamd 容器加内存上限并降低最大线程数,这是我的常用配置:

services: clamd: mem_limit: 2048m environment: - MAX_THREADS=1

MAX_THREADS=1不是让 ClamAV 只用一个线程,而是限制它单次扫描线程的并发量,实际性能损失不大,内存压力却降得立竿见影。加完docker compose up -d重新创建容器即可生效。

第五道坎:收件人看到邮件时间差 8 小时。
现象:同一封邮件,Webmail 里显示时间正常,但在 Outlook 里看日期头是 UTC 时间。
原因:两个层面,服务器时区没设为 Asia/Shanghai,并且 Dovecot 投递时没有正确写入本地时区头。
解决:mailcow.conf里把TZ=Asia/Shanghai改好,然后进 Web UI 的「系统 → 设置」把默认时区也改成 Asia/Shanghai,最后重启 postfix 和 dovecot 容器。如果你在部署时就设置了正确的TZ,这步可以完全跳过。

6. 进阶:怎么确认自己的邮件真的进了对方收件箱

部署完成只是开始,邮件服务器上线后真正见真章的是投递率。我的习惯是把第 4 章的三件套配好并确认生效后,先给mail-tester.com发一封测试邮件,它会从外部视角检查发送方 IP 的 PTR、SPF、DKIM、DMARC 各项评分,给一个 10 分制的分数。8 分以下说明还有硬伤,9 分以上基本具备正常投递的资格。同时把域名加到 Google Postmaster Tools 里,这个后台能看到 Gmail 方向的送达率、垃圾邮件率、IP 信誉三个核心指标,比到处问“为什么进垃圾箱”可靠得多。

Mailcow 的 Web UI 自带一个「诊断」页面,位置在「系统 → 诊断」,点运行后会从本机视角测试 25 端口连通性、PTR 记录、证书有效性等基础项。这个诊断只能证明你的服务端口都通,不能证明外部信任你,所以它适合做排障起点而不是终点。

有段时间我把 DKIM 密钥轮换了一轮,改完 DNS 记录后直接去跑 mail-tester,分数从 9.5 掉到 6,排查了半天才发现是密钥轮换后 Rspamd 缓存没清透,DNS 那边其实已经生效了,走了一遍第 5 章那个 Redis 清理命令再测试才回到正常。从那以后我每次涉及 DNS 或密钥的变更,强制走一遍固定的验证顺序:先dig确认解析,再重启 rspamd 容器清 Redis,最后用外部邮件地址实际收发各一封确认投递状况。

这套流程记录得越详细,越容易在后续排查中定位问题域。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询