
简介Mailcow是基于Docker容器化技术构建的开源邮件服务器整合方案将SMTP/IMAP/POP3/Webmail、反垃圾、反病毒以及DKIM/DMARC/SPF安全认证统一封装适用于需要自主掌控邮件数据的运维工程师、中小型企业IT部门及邮件系统二次开发者。资源共2000个文件核心为1515个PHP源码文件支撑Webmail界面与API逻辑另有Shell/Python脚本、Dockerfile与YAML编排文件便于完成容器部署、定时任务与自动化维护JSON/XML/conf类文件则承载服务配置和日志收集规则Markdown文档提供使用说明。压缩包仅10.98MB整体精简目录清晰。已有200人学习下载适合快速搭建邮件环境或研读容器化部署细节。通过源码和配置可了解邮件收发链路、密钥管理及SPF/DKIM/DMARC校验的实际落地为自建邮件系统的运维排错提供直接参考。1. Mailcow不是又一个邮件系统它把半个邮件机房塞进了 Docker Compose如果你只是想给一个小团队配上可用、可控的自建邮件服务可以先看一眼传统路径Postfix负责收发投递Dovecot管IMAP/POP3Rspamd拦垃圾信ClamAV扫毒SOGo或LDAP提供通讯录和账号再写一堆脚本让它们互相认识。等你把这一套手工拼完往往发现最大的成本不是安装本身而是日后的维护。Mailcow是一个功能丰富的开源邮件服务器解决方案把这些组件固化进一套Docker编排用几条命令就能把整套邮箱系统拉起来。它适合不想为邮件搭建和维护细节耗时间又希望邮箱真正掌握在自己手里的团队和个人。我在还只会手工搭Postfix的年代光是把SPF、DKIM、DMARC三条记录调到让Rspamd不打负分就花了一个周末。换到Mailcow之后这套组合默认就带着反垃圾、杀毒、Web邮箱和管理后台部署节奏完全变了。下面这些内容按我从零上手到接手生产的顺序来写新手能照着跑通熟手可以直接看参数和坑。2. 动手前先看懂这套架构容器清单、端口占用与资源预算Mailcow看起来是一条命令启动但它内部不是一个单体而是一组协作的容器。如果你不明白每个容器负责什么排障时会很被动Dovecot报错你会去看Postfix日志Rspamd拒绝收信你又去找ClamAV来回浪费好几个小时。所以我建议先花十分钟把架构立住再动手安装。2.1 容器全家桶Postfix、Dovecot、Rspamd 在 Mailcow 里分别是干哪一块的默认情况下Mailcow 管理着下面这些容器各自的角色基本稳定容器职责我一般什么时候会用到它nginx提供 Web 入口和 TLS 终结管理界面、SOGo、API 都从它进来打不开控制台、证书异常时postfixSMTP 收发投递是邮件进出的大门查队列、看退信、排查收不到信dovecotIMAP/POP3 服务以及邮件存储和检索客户端连不上、迁移邮箱时rspamd垃圾邮件评分承担 DKIM/ARC 签名看为什么进垃圾箱、调整过滤clamd病毒扫描发现带毒附件时sogoWeb 邮箱、日历、通讯录用户说网页邮箱打不开phpfpm运行 Mailcow 管理后台的 PHP 逻辑后台报 502 时查它mysql域名、邮箱账号、别名、配置等元数据备份、恢复、批量建账号redis缓存给 Rspamd 和 UI 做辅助内存不足时先考虑它unbound本地 DNS 解析缓存系统 DNS 慢导致投递延迟时acme申请和续签 Lets Encrypt 证书证书明明存在却过期了watchdog检查容器健康状态异常时自动拉起容器反复重启时看它的判定这套分工不是随意的Postfix 负责对外说“我能收信”Dovecot 负责“收进来的信现在能取走”中间无数封被判定为垃圾或带毒的信件由 Rspamd 和 ClamAV 在 Postfix 一侧拦截。Mailcow 把这条链路写死在 Compose 编排里让它从一堆服务变成了一个可复制、可恢复的整体。这对运维的好处很明显单容器崩溃可以被 watchdog 拉起来日志通过 docker compose logs 统一查看改动参数不需要逐个改配置文件多数入口都收敛到了后台和 mailcow.conf。坏处是你要接受它的整体性不能随便替换某个组件版本所以后面会反复提到“固定镜像 tag”。2.2 内存磁盘与 25 号端口的账部署前先算清三笔账第一笔账是内存。官方最低要求 4GB但真实跑起来我建议至少 6GB。我见过一台 4GB 云主机装完 MailcowMySQL、Redis、Rspamd、ClamAV 一起预热内存曲线直接顶到 3.8GB再来一次全量杀毒就触发 swap。如果还要跑 SOGo 并让几十人同时用网页邮箱8GB 会更稳。第二笔账是磁盘。下载镜像并完成首次启动后系统盘占用大约在 1GB 到 2GB 之间具体看你拉的是完整版本还是精简版。这还只是开始邮件正文和附件会持续增长MySQL 里保存账号和域名配置日志会写满 /var/lib/docker 目录。我习惯把容器数据目录放在独立分区或 LVM 上这样磁盘增长时可以扩容不会因为一个分区满了带着整个系统动不了。第三笔账是端口。Mailcow 默认要绑定以下端口端口用途25SMTP 收信出站投递也用它80HTTP 跳转和 ACME 验证443HTTPS 管理界面和 SOGo587邮件客户端提交发信143 / 993IMAP / IMAPS110 / 995POP3 / POP3S最容易被忽视的是 25 端口。很多云厂商默认不开放入站 SMTP你在服务器上telnet mail.example.com 25是通的但外域投递进来直接 Connection refused。遇到这种情况先找服务商开放 25 端口同时把服务器 IP 的反向 PTR 记录做好否则即使开了端口对方也可能因为 PTR 缺失而降分拒收。另外如果这台机器上已经跑了 Nginx 或其他 Web 服务80 和 443 会被占用。启动前先用ss -lntp | grep -E :(80|443)检查一下避免起容器时端口冲突。当时我在一台跑着监控面板的机器上装 Mailcow就是因为 443 被占折腾了二十分钟才找到原因。3. 用 Compose 把 Mailcow 跑起来最小部署与四个必调参数这一章的目标是让你在一台干净的系统上把 Mailcow 拉到能创建邮箱、能正常收发。中间我会把每一步背后的逻辑讲清楚而不是给你一串黑匣子命令。3.1 拿到源码包之后生成配置、改 hostname 和时区最常见的做法是把 Mailcow 的官方仓库 clone 到 /opt 下。仓库名是 mailcow-dockerizedclone 完进入目录后先执行它的配置生成脚本cd /opt git clone https://github.com/mailcow/mailcow-dockerized.git cd mailcow-dockerized ./generate_config.sh这个脚本会交互式问几个问题最重要的是邮件服务器的主机名它决定了你对外的 SMTP 欢迎头和 Web 地址。注意这里填的不是域名裸域而是一个子域比如 mail.example.com。填错了后面很多配置都要连带改所以宁可在这一步想清楚。脚本跑完会生成 mailcow.conf。我一般会立刻检查几个参数nano mailcow.conf关键参数有三个。MAILCOW_HOSTNAME是刚才填的邮件服务器主机名必须是一个能解析到公网 IP 的 A 记录地址。TZ是时区国内服务器建议改成 Asia/Shanghai否则日志和后台时间会差八个小时。还有一个是HTTP_PORT和HTTPS_PORT如果你没有让出 80 和 443可以在这里改到高位端口但我建议最好还是把原来的服务挪走否则后面证书申请和邮件接收都会遇到额外麻烦。3.2 三行命令启动看懂 compose up 背后的初始化流程配置生成后启动只需要三条命令docker compose pull docker compose up -d docker compose psdocker compose pull会把所有容器镜像拉全。Mailcow 使用的镜像比较多这一步在部分网络环境下可能很慢建议先保证 Docker Hub 可以正常访问再开始部署。docker compose up -d是正式创建并启动容器组第一次启动时 MySQL 会在 /mailcow-dockerized 数据目录里初始化库watchdog 会检查其他容器是否健康。最后docker compose ps用来确认所有容器处于 Up 状态。如果启动过程中某容器反复重启看日志是第一步docker compose logs -f mysql docker compose logs -f watchdog比如我遇到过 MySQL 起不来原因是数据目录权限在之前的手动启动中被打乱日志里直接显示权限错误。这时用chown -R root:root或调整目录属主后重启即可。不要急着把所有容器删掉先定位哪个容器不健康。首次启动完成后浏览器打开 https://mail.example.com。默认管理员账号是 admin初始密码是 moohoo第一次登录会强制你改成自己的强密码。这个默认密码是公开知识所以最好在部署完成后立即修改不要留到第二天。3.3 DNS 接管与首次登录MX、SPF、DKIM、DMARC 的落地样板后台登录后在“配置”里添加上你的域名例如 example.com。Mailcow 会为该域名自动生成 DKIM 密钥并在域名管理页面列出它要求的 DNS 记录。你不需要凭记忆手写直接把页面里的记录抄到 DNS 服务商即可。以 example.com 为例完整记录至少包含类型主机/名称值作用Amail你的服务器公网 IP让 mail.example.com 可解析MXexample.commail.example.com优先级 10别人知道该往哪里投递TXTexample.comvspf1 a mx ip4:服务器IP -all授权合法发信 IPTXTdefault._domainkey.example.comvDKIM1; krsa; p...给收件方验签TXT_dmarc.example.comvDMARC1; pquarantine; ruamailto:postmasterexample.com接收方按 DMARC 策略处理注意 SPF 里的ip4:一定要写这台服务器实际发信的出口 IP。如果邮件用 Mailcow 直发不要使用某些云厂商的默认 SPF否则会把自己排除在合法发信源之外。配置完成后我一般用几条命令确认dig MX example.com dig TXT default._domainkey.example.com dig TXT example.com如果这些记录都返回了对应值再用一个外部邮箱给这台邮件服务器发信测试。此刻不要急着通知团队切 MX先把收发链路验证完尤其是外域来信能不能进收件箱、发出去的邮件会不会进垃圾箱。4. 邮件服务器避坑部署 48 小时内最容易翻车的五个地方邮件服务器不像普通 Web 服务不是监听了端口就算成功。它需要接受来自全世界的投递和检测而每个环节都可能因为一个小配置而失败。以下五个坑是我在 Mailcow 和手工邮件系统里都踩过的每一条都按现象、原因、解决来写。4.1 收不到外域邮件先查 PTR、25 端口与发件 IP 信誉现象内部互相发信正常但 Gmail、163 等外部邮箱给你发信时收到退信退信内容常见 “Connection refused” 或 “host does not accept email”。原因有两个。第一是服务器所在机房的 25 端口入站没有放行第二是 IP 反向解析缺失对方在连接前查 PTR 发现 mail.example.com 和你的 IP 对不上直接拒绝连接。解决先到服务器上看 25 端口在不在监听ss -lntp | grep :25如果监听正常再用本机测试telnet mail.example.com 25连接成功后会返回带主机名的 SMTP 欢迎语。如果连接超时去机房控制台或找服务商放行 25 端口。接着做反解到服务商后台为这个 IP 配置 PTR值为 mail.example.com。PTR 解析生效需要一段时间通常几小时到一天配置完可以用dig -x 你的服务器IP验证。4.2 发信进垃圾箱SPF 没生效与 Rspamd 评分被抬高的连锁反应现象你用 Mailcow 发出去的邮件能送达但收件人那边直接进了垃圾箱。尤其发往 Gmail 或腾讯企业邮箱时概率很高。原因SPF 记录配置错误或未生效使得你的发件 IP 被判定为未授权另一个隐性原因是服务器公网 IP 此前被用于群发段位信誉已经不好。Mailcow 里 Rspamd 会在投递前对邮件做 DKIM 签名但如果 SPF 检查不过DMARC 对齐也会失败整体分数被打得很高。解决在 DNS 服务商确保 SPF 记录包含该服务器的 A 记录和 IP。我见过最坑的写法是vspf1 -all这等于宣布自己从来不发信Mailcow 投出的信自然被外部拒绝。修正为下面的常见写法vspf1 a mx ip4:你的服务器IP -all改完等 DNS 生效后在 Rspamd 后台的 History 里看正在发出的邮件确认 SPF 和 DKIM 两项显示 pass。如果 IP 信誉确实差短期内只能更换发信 IP或者先把 DMARC 从 pquarantine 提到更加严格的策略等信誉恢复。4.3 Web 管理界面打不开443 被占用和证书没签出来的区别现象启动后 docker compose ps 全部正常但浏览器访问 https://mail.example.com 提示安全连接失败或者干脆一直转圈。原因一是服务器上已有 Nginx 或其他服务占用了 443Mailcow 的 nginx 容器无法绑定只能在内部进入外部访问撞到旧服务二是 acme 容器在首次申请证书时失败导致 Nginx 没有拿到 ssl_certificate。解决先看 nginx 容器日志docker compose logs -f nginx如果看到 bind: address already in use说明 443 被占。最简单的做法是把占用 443 的服务停掉或把 Mailcow 的 HTTPS_PORT 改到 8443但 8443 会给后续收信和客户端配置增添变数最佳方案还是把端口让出来。如果是证书申请失败日志里会显示 acme_challenge 失败比如 HTTP-01 验证无法完成。原因通常是域名的 A 记录还没指向这台服务器或 80 端口没有映射。检查 DNS 记录确实指向本机后重启 acme 容器重新签发docker compose restart acme证书申请是异步的重启后等几分钟再刷新页面。4.4 日志和临时文件把磁盘写满Docker 日志轮转总要自己动手现象运行一两个月后系统突然提示磁盘满docker 目录占用巨大邮箱收信开始失败。原因Docker 默认不限制单个容器日志文件大小。Nginx、Postfix、Rspamd 每天都会产生大量标准输出日志/var/lib/docker/containers/ 下的 json.log 能长到几 GB。加上 ClamAV 的病毒库和临时文件磁盘很快被吃光。解决在不重启所有容器的前提下可以先把当前日志置空truncate -s 0 /var/lib/docker/containers/*/*-json.log但这是临时止血。持久做法是修改 Docker daemon 配置在 /etc/docker/daemon.json 里加入{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }然后重启 Docker 服务让配置生效。已经存在的容器不会自动读取新配置下次重建容器时才会应用。同时建议对 /var/lib/docker 做一次周期性监控或者直接在服务器上写一个 cron 每天 truncate 一次各容器日志。4.5 升级大版本爆红备份 MySQL 与固定镜像 tag现象看到 Mailcow 有新版直接docker compose pull后重启界面开始报 502或 MySQL 连接失败。原因全量与中间版本之间可能有数据库结构变更你跳过了中间版本且运行中的 MySQL 数据与新版镜像不兼容。另一个常见原因是有人把 mailcow.conf 里的镜像版本改成了 latest一拉就拉到未来版本。解决升级前先备份。常见做法是使用 Mailcow 自带的 helper 脚本生成完整备份包具体命令下一章会讲。升级时不要直接 pull 所有镜像而是看官方更新说明按当前大版本逐步升。固定镜像 tag 的做法是查看 docker-compose.yml 中每个镜像的具体版本标签不要手动改成 latest。如果已经升级失败最快的后悔药是从刚才的备份恢复 MySQL 数据容器再回到原 tag 启动。备份文件要同时回滚否则邮件数据新、数据库旧Dovecot 和 Postfix 之间也会对不上账号结构。5. 让 Mailcow 变成生产力API 批量建号、备份恢复与性能线到这一步邮件服务器已经能正常收发。接下来要做的是把它从“能用的玩具”变成“能托付业务的工具”。对于管理员来说最花时间的不是装系统而是反复建账号、备份、调性能。5.1 用 Mailcow API 批量建邮箱给 300 个新同事发账号的脚本Mailcow 自带了 API后台在“系统”菜单里可以创建 API Key。常见用法是给一个只读或可写 Key然后通过 HTTP 请求管理域名和账号。我一般用 curl 验证单个账号是否能创建成功curl -sS -X POST https://mail.example.com/api/v1/add/mailbox \ -H X-API-Key: 你的API_KEY \ -H Content-Type: application/json \ -d { domain: example.com, local_part: zhangsan, password: Init2025, password2: Init2025, active: 1, quota: 10 }这里的local_part是 前面的部分quota单位是 GBactive为 1 表示启用。注意不同版本对参数名可能略有调整我建议先用后台手工建一个账号再对比 API 返回的 JSON 结构确认字段没问题后再批量跑。批量创建时我习惯先准备一个 CSV 或读取一个名字列表import requests url https://mail.example.com/api/v1/add/mailbox headers {X-API-Key: 你的API_KEY, Content-Type: application/json} users [ {local_part: user01, password: Init2025}, {local_part: user02, password: Init2025}, ] for user in users: payload { domain: example.com, local_part: user[local_part], password: user[password], password2: user[password], quota: 10, active: 1, } r requests.post(url, jsonpayload, headersheaders, verifyFalse) print(user[local_part], r.status_code, r.text)verifyFalse是因为在某些环境中 API Key 对应的证书链不完整生产环境建议去掉它并把证书配好。批量建号最大的坑是密码策略Mailcow 默认对密码长度有要求脚本里如果生成太短的密码API 会返回校验失败最好在脚本里统一按 8 位以上混合密码生成。5.2 备份不是 tar 一个目录helper.sh 的真正用法与恢复演练很多管理员第一次备份 Mailcow就是把/opt/mailcow-dockerized整个目录 tar 一下。这样备份不是完全没用但恢复时很可能发现账号和域名对不上因为 MySQL 数据目录和邮件附件并不在同一层只 tar 当前目录容易漏掉 Docker volume 里的实际数据。官方 helper 脚本是更省心的备份方式cd /opt/mailcow-dockerized ./helper.sh backup执行完成后脚本会在 backup 目录下生成一个带日期时间戳的 tar 包里面包含了 MySQL 逻辑备份、密钥配置、账号列表和附件数据。由于内容较多第一次备份时磁盘 IO 会明显升高建议放在业务低峰期跑。恢复同样走 helper./helper.sh restore /opt/mailcow-dockerized/backup/mailcow-2025-06-01-XXXX.tar.gz恢复过程会先停止关键容器把备份包里的数据目录和数据库释放回当前位置再重新启动。执行前先把当前目录另存一份避免恢复出错后连回到原状态的机会都没有。我个人的习惯是每周全量备份一次每天用 cron 把 helper 生成的备份包同步到另一台服务器或对象存储。邮件服务器最先丢的不是机器而是数据。恢复包如果只存在同一块磁盘上磁盘损坏时根本来不及后悔。每月至少做一次恢复演练恢复到一台临时虚拟机打开管理界面看账号数量和最后一封邮件是否完整。5.3 性能调优的三个调节阀邮件大小、Rspamd 内存与连接数邮件服务器很少需要极限调优但三个参数值得先看一眼。第一个是邮件大小上限。Postfix 默认限制单封邮件大小如果团队经常发大附件会发现投递频繁被退。Mailcow 把这个参数收敛到了 mailcow.conf 里常见做法是设为 25MPOSTFIX_MESSAGE_SIZE_LIMIT25改完运行docker compose up -d让 postfix 容器重新读取。注意邮件大小限制不止出现在发送端客户端也要认这个上限别把 Mailcow 当网盘。第二个是 Rspamd。Rspamd 对内存比较敏感在高并发扫描时它会占用不少内存。如果观察到系统内存长期告警常见做法是减少 Rspamd 并发 worker 数量或在后台关闭不需要的过滤器。不要一味增加 worker往往效果不大反而让 MySQL 的缓存被压缩整体更慢。第三个是连接数。Dovecot 默认的连接数对于百人团队是绰绰有余的真正需要调的是客户端短连接频繁建立带来的新建连接压力。我一般会建议客户端保持长连接并在同一台机器上把文件描述符上限调到足够大ulimit -n 65535检查连接数最简单的方法是看 Dovecot 日志里有没有 “Too many open files”。如果频繁出现先调系统限制再调 Dovecot 配置。四个容器同时日志警告时先加文件描述符再考虑换更大的机器。6. 把旧邮箱迁进 Mailcow一条 dsync 命令和我的迁移顺序Mailcow 已经能收发新信但很多团队手里还有旧邮箱里的历史邮件要保留。迁移这些老邮件我推荐用 imapsync 而不是在客户端里拖拽。客户端拖拽几百封没问题一旦超过上万封断点续传和去重会让你崩溃。先在新 Mailcow 上把邮箱账号建好然后执行imapsync \ --host1 oldmail.example.com --user1 olduserexample.com --password1 旧密码 \ --host2 mail.example.com --user2 olduserexample.com --password2 新密码 \ --addheader --ssl1 --ssl2host1是旧邮箱服务器host2是 Mailcow 的地址--addheader会在迁移的邮件头里增加一行标记方便后续查重和排障。--ssl1 --ssl2表示两端都用加密连接。首次迁移会遍历全部目录和邮件耗时和邮件数量成正比建议放到夜间执行。我自己的迁移顺序是先在 Mailcow 建一个测试账号迁一部分邮件进来客户端连上用两天确认主要功能没问题后再把旧域名的 MX 记录切到 MailcowMX 切换生效后再对剩余账号做一次增量同步把旧服务器上新增的邮件补过来。不要直接改 MX 再慢慢迁这样新旧服务器都会收到信邮件会裂成两半。切换 MX 前三天我通常会把旧记录的 TTL 从默认的 3600 调低到 600。这样如果 Mailcow 这边出了状况回滚旧服务器的速度会快得多。等全部账号增量同步完成、旧服务器确认没有遗漏后再删掉旧账号和旧的 DNS 记录。跑完一段迁移后你会发现真正让你放心的不是 imapsync 的参数而是每一步都有回退路径。希望帮到你。本文还有配套的精品资源点击获取