📧 保姆级教程:Mailcow 企业邮件服务器部署(Debian 12)

依据: Mailcow 官方安装文档 + 官方 DNS 文档
官方安装指南: https://docs.mailcow.email/getstarted/install/
官方 DNS 指南: https://docs.mailcow.email/getstarted/prerequisite-dns/
官方 Relayhost 文档: https://docs.mailcow.email/post_install/relayhosts/
操作系统: Debian 12(适用 Ubuntu 24.04+)
预估耗时: 60~120 分钟(含排错)
📋 完整操作流程一览
1. 开放服务器防火墙端口 ↓ 2. 配置域名 DNS(部署前必须做) ↓ 3. SSH 连接服务器 ↓ 4. 安装系统依赖包 ↓ 5. 安装 Docker + Docker Compose ↓ 6. 下载 Mailcow 代码 ↓ 7. 执行 generate_config.sh(交互式问答) ↓ 8. 修改 mailcow.conf(时区 + SAN + 跳过健康检查) ↓ 9. docker compose pull ↓ 10. docker compose up -d(启动) ↓ 11. 用 admin / moohoo 登录面板 ↓ 12. 配置 SPF / DKIM / DMARC / PTR ↓ 13. 修复容器 DNS(Unbound 转发器) ↓ 14. 修复 HTTPS 证书(certbot 方案) ↓ 15. 排查出站 25 端口是否被封 ↓ 16. 配置 SMTP 中继(解决出站 25 端口封锁) ↓ 17. 验证收发邮件 ↓ 18. 日常运维
1. 准备工作
1.1 你需要什么
| 项目 | 说明 |
|---|---|
| 服务器 | Debian 12 或 Ubuntu 24.04,最低 2C2G(推荐 2C4G) |
| 域名 | 例如 example.com |
| SSH 客户端 | Windows 用 Putty,Mac/Linux 用终端 |
| 端口 | 在云安全组放行 22、25、80、443、587、465、993、995、4190 |
1.2 端口说明
| 端口 | 用途 | 说明 |
|---|---|---|
| 22 | TCP | SSH |
| 25 | TCP | SMTP 发信 |
| 80 | TCP | HTTP 证书验证 |
| 443 | TCP | HTTPS 管理面板 |
| 587 | TCP | SMTP 发信(客户端) |
| 465 | TCP | SMTP SSL 发信 |
| 993 | TCP | IMAP SSL 收信 |
| 995 | TCP | POP3 SSL 收信 |
| 4190 | TCP | Sieve 过滤管理 |
⚠️ TCP vs UDP 特别说明:SMTP(发邮件)、IMAP(收邮件)、HTTP/HTTPS(网页)全部使用的是 TCP 协议,不是 UDP。云安全组规则中放行端口时,确保协议选择的是 TCP。如果误开 UDP 25 端口,对邮件收发没有任何作用。UDP 主要用于 DNS 查询(53端口)、NTP 时间同步(123端口)等场景,邮件收发不涉及 UDP。
1.3 本教程使用的示例
域名:
example.com(请替换为你的真实域名)服务器 IP:
<你的服务器IP>(请替换为你的真实 IP)
2. 域名 DNS 配置(部署前必须完成)
2.1 官方最小 DNS 配置
Mailcow 官方文档明确要求,部署前必须配置以下 DNS 记录。
这些记录决定了:
A 记录:别人能找到你的邮件服务器在哪
CNAME:邮件客户端能自动发现服务器配置
MX 记录:别人知道发往
@example.com的邮件该送到哪台服务器
2.2 登录域名管理后台
| 域名注册商 | 入口 |
|---|---|
| 阿里云(万网) | 控制台 → 域名 → 点击域名 → 解析设置 |
| 腾讯云(DNSPod) | 控制台 → 域名管理 → 解析 |
| Cloudflare | Dashboard → 选择域名 → DNS → Records |
| 其他 | 找「DNS 管理」或「域名解析」 |
2.3 添加 A 记录
记录类型:A主机记录:mail 记录值:<你的服务器IP> TTL:默认
生成的完整域名:
mail.example.com→ 你的服务器IP
2.4 添加 CNAME 记录(两条)
记录① autodiscover:
记录类型:CNAME主机记录:autodiscover 记录值:mail.example.com. TTL:默认
记录② autoconfig:
记录类型:CNAME主机记录:autoconfig 记录值:mail.example.com. TTL:默认
⚠️ 记录值末尾必须加 **
.**(点号),即mail.example.com.
2.5 添加 MX 记录
记录类型:MX 主机记录:@ 记录值:mail.example.com. 优先级:10 TTL:默认
⚠️ 同样末尾加
.:mail.example.com.
2.6 验证记录生效
nslookup mail.example.com nslookup -type=mx example.com nslookup autodiscover.example.com
3. SSH 连接服务器
3.1 Windows(Putty)
下载打开 Putty
Host Name = <服务器IP>,Port = 22点击 Open → Accept
输入
root+ 密码
3.2 Mac / Linux
ssh -p 22 root@<服务器IP>
4. 安装系统依赖包
apt update && apt upgrade -y
apt install -y git openssl curl gawk coreutils grep jq certbot python3-certbot-nginx
这 8 个包是 Mailcow 官方列出的必需依赖。
gawk(awk)、coreutils(包含 sha1sum、cut 等)、jq(JSON 解析,2025-09 版本起新增)。**certbot和certbot-nginx** 用于后续自动申请 HTTPS 证书(第14章)。
5. 安装 Docker + Docker Compose
5.1 安装 Docker 引擎(官方一键脚本)
curl -sSL https://get.docker.com/ | CHANNEL=stable sh
⚠️ 官方特别强调:必须使用最新版 Docker Engine,不要用系统自带源中的旧版。 如果
curl超时,先配代理或重试几次。
5.2 启动 Docker 并设置为开机自启
systemctl enable --now docker
5.3 验证 Docker
docker --version
应显示 Docker version 27.x.x, build xxxxx 或更高版本。
5.4 安装 Docker Compose 插件
apt update && apt install -y docker-compose-plugin
如果使用
get.docker.com一键脚本,它已经自动安装了 compose 插件,但这步做了也不会错。
5.5 验证 Docker Compose
docker compose version
应显示 Docker Compose version v2.x.x。
⚠️ 注意:Docker Compose 插件版的命令是 **
docker compose**(中间有空格,没有连字符)。
6. 下载 Mailcow
suumask 0022cd /opt git clone https://github.com/mailcow/mailcow-dockerizedcd mailcow-dockerized
su确保以 root 身份操作。umask 0022确保文件权限为 755(目录)/ 644(文件),容器才能正常读取。
7. 生成配置文件
7.1 启动配置生成脚本
./generate_config.sh
执行后屏幕上会显示提示文字,让你输入邮件服务器的主机名。
你会看到:
Press enter to confirm the detected value '[value]' where applicable or enter a custom value. Mail server hostname (FQDN) - this is not your mail domain, but your mail servers hostname:
📌 这里要填什么?
这里的 FQDN(完全限定域名) 指的是你的邮件服务器完整域名,格式为 子域名.主域名.顶级域。
你买的域名是
example.comFQDN 应填写
mail.example.com这个域名后续用于:访问管理面板、SMTP/IMAP 服务、SSL 证书申请
不要填 example.com(缺少子域名),不要填完整 URL(如 https://...)。
7.2 填写邮件域名(第1个问题)
→ 输入 mail.example.com 然后回车
必须包含子域名(如
mail.),不能只输入example.com。FQDN 示例:mx.gmail.com中的mx就是子域名。
Q2 — 时区
Timezone [Etc/UTC]:
→ 国内服务器输入 Asia/Shanghai 然后回车
直接回车则保留
Etc/UTC,后续可以通过修改mailcow.conf来更改。
Q3 — 选择分支
Which branch of mailcow do you want to use? Available Branches: - master branch (stable updates) | default, recommended [1] - nightly branch (unstable updates, testing) | not-production ready [2] - legacy branch (supported until February 2026) | deprecated, security updates only [3] Choose the Branch with it's number [1/2/3]:
→ 输入 1 然后回车(master 稳定版,生产环境首选)
Q4 — ClamAV(仅当内存 ≤ 2.5GB 时出现)
如果你的服务器内存大于 2.5GB,不会出现此问题,自动跳过。
7.3 问答结束后脚本自动完成的内容
IPv6 not detected on host – disabling IPv6 support.Skipping Docker IPv6 configuration because host does not support IPv6.IPv6 configuration complete: ENABLE_IPV6=falseGenerating snake-oil certificate... ...+.........++++++++++++ -----Copying snake-oil certificate... root@你的主机名:/opt/mailcow-dockerized#
回到命令行提示符,表示配置文件已生成完毕。
7.4 检查生成的配置文件
cat mailcow.conf
确认包含以下内容:
MAILCOW_HOSTNAME=mail.example.comHTTP_PORT=80HTTPS_PORT=443TZ=Asia/Shanghai
8. 修改配置文件
这一步不是官方强制的,但根据实际部署经验,有五个地方建议调整,否则会遇到各种问题。
8.1 修改时区(如果上一步选择了 Etc/UTC)
为什么改: Etc/UTC 比北京时间晚8小时,不改也能用,但邮件时间、日志时间都会差8小时。改成 Asia/Shanghai 后所有时间都显示为北京时间,排查问题方便。
sed -i 's|TZ=.*|TZ=Asia/Shanghai|' mailcow.conf
如果上一步生成配置时已经选
Asia/Shanghai,这步可以跳过。
8.2 添加附加域名(方便邮件客户端自动发现)
为什么加: DNS 里配了 autodiscover 和 autoconfig 两条 CNAME。如果不在 SSL 证书里包含这两个域名,Outlook 或 Thunderbird 自动配置时会弹出安全警告说证书不匹配。加上这行后 Let's Encrypt 申请的证书会同时覆盖这三个域名。
echo 'ADDITIONAL_SAN=autodiscover.example.com,autoconfig.example.com' >> mailcow.conf
8.3 跳过 Unbound 健康检查(防止部署失败)
为什么加: Mailcow 默认自带一个 DNS 解析器叫 Unbound,MySQL 容器启动时依赖它。但在国内云服务器上,Unbound 经常 DNS 查询超时而无法通过健康检查,导致 MySQL 初始化失败。
MySQL 没初始化,admin 账号就写不进数据库。结果就是你打开面板输入 admin / moohoo 时登录失败。
加了这行: Mailcow 跳过 Unbound 的健康检查,直接用系统 DNS。MySQL 正常初始化,admin 账号正常创建,不会再登录失败。
echo 'SKIP_UNBOUND_HEALTHCHECK=y' >> mailcow.conf
8.4 跳过 IP 地址检查(防止 acme 容器启动失败)
为什么加: acme-mailcow 容器启动时会自动检测你的服务器公网 IP,并尝试将域名解析结果与公网 IP 匹配。在某些云服务商环境下(特别是 NAT 或非标准网络),检测会超时或失败,导致 Let's Encrypt 证书申请跳过。
加了这行: 跳过公网 IP 检测,按时尝试申请证书。如果证书申请失败,可以后续用手动方式修复(见第14章)。
echo 'SKIP_IP_CHECK=y' >> mailcow.conf
8.5 确认修改结果
grep -E "MAILCOW_HOSTNAME|HTTP_PORT|HTTPS_PORT|TZ|ADDITIONAL_SAN|SKIP_UNBOUND|SKIP_IP" mailcow.conf
应显示:
MAILCOW_HOSTNAME=mail.example.comHTTP_PORT=80HTTPS_PORT=443TZ=Asia/ShanghaiADDITIONAL_SAN=autodiscover.example.com,autoconfig.example.comSKIP_UNBOUND_HEALTHCHECK=ySKIP_IP_CHECK=y
9. 启动 Mailcow
9.1 拉取 Docker 镜像
docker compose pull
这步做什么: 从 Docker Hub 下载 Mailcow 运行所需要的全部容器镜像,包括 nginx(网页服务器)、mysql(数据库)、postfix(发件)、dovecot(收件)、rspamd(反垃圾)、sogo(Webmail)等十几个组件。每台服务器只需下载一次。
⏱ 根据网速需要 5~20 分钟。
国内服务器镜像加速: 如果 pull 卡住不动,是因为 Docker Hub 在国内被限制。配置镜像加速器后重新 pull:
mkdir -p /etc/dockercat > /etc/docker/daemon.json << 'EOF'{ "registry-mirrors": [ "https://docker.xuanyuan.xyz", "https://docker.1ms.run", "https://docker.m.daocloud.io"]} EOF systemctl restart docker docker compose pull
9.2 启动所有容器
docker compose up -d
这步做什么: 根据 docker-compose.yml 文件的定义,按依赖顺序依次创建并启动所有容器。-d 参数表示后台运行,关闭 SSH 后服务也不会停止。
9.3 检查容器状态
等待 30~60 秒让容器完成初始化,然后:
docker compose ps
怎么看结果: STATUS 列显示 Up 或 Up (healthy) 表示正常启动。如果某个容器显示 unhealthy 或 Exited,说明有问题需要排查。
10. 登录管理面板
10.1 浏览器访问
https://mail.example.com
首次访问会提示「您的连接不是私密连接」,因为用的是自签名证书。点击高级 → 继续前往即可。正式证书申请(见第14章)之后就不会再提示了。
10.2 官方默认登录凭据
| 字段 | 值 |
|---|---|
| 用户名 | admin |
| 密码 | moohoo |
官方文档原文:*"You can now access https://${MAILCOW_HOSTNAME}/admin using the default credentials admin and the password moohoo."*
10.3 登录成功后立即修改密码
面板右侧菜单 → 系统 → 配置 → 权限管理 → 编辑,然后修改密码保存。
10.4 😱 登录失败怎么办?
如果 admin / moohoo 提示错误:
# 重置 admin 密码为 moohoocd /opt/mailcow-dockerized
docker compose exec -T mysql-mailcow mysql -uroot -p$(grep -oP 'DBROOT=\K.*' mailcow.conf) -e \ "USE mailcow; REPLACE INTO admin (username, password, superadmin, active) VALUES ('admin', '\2y\10\$NLRS/FE07kqBWB5cGNjPOeKcxS9C8VTJPi8VQC8q5NQ.RWYqnPwK6', 1, 1);"执行后密码重置为 moohoo,刷新页面重新登录。
11. 配置邮箱域名
Mailcow 只是个邮件服务器框架,它不知道你要管理哪个域名、要给谁开邮箱。你得告诉它:你要添加
example.com这个域名,然后在这个域名下创建具体的邮箱账号。
11.1 添加域名
为什么做这步: 告诉 Mailcow 你要管理 example.com 这个域名的邮件。只有添加了域名,才能在这个域名下创建邮箱。
面板右侧菜单 → E-Mail→ 配置 → 域名 → 添加域名
| 字段 | 值 |
|---|---|
| 域名 | example.com |
| 描述 | 可为空或填备注 |
| 最大允许的邮箱数 | 10000 |
⚠️ 其他默认,并且点击「添加域名并重启」按钮(不是「添加域名」)。
11.2 创建邮箱
为什么做这步: 添加域名后,需要给具体的人开邮箱账号,这样他才能在 Webmail 或手机邮件客户端上收发邮件。一个域名可以创建几百个邮箱,每个邮箱对应一个员工。
面板右侧菜单 → 配置 → 邮件设置 → 邮箱 → 添加邮箱
| 字段 | 示例 | 说明 |
|---|---|---|
| 用户名(邮箱地址左侧的部分) | zhangsan | 邮箱前缀,@ 前面的部分 |
| 域名 | example.com | 选择刚添加的域名 |
| 全称 | 张三 | 显示名称,收件人看到的发件人姓名 |
| 密码 | 设置强密码 | 邮箱密码,以后登录要用 |
| 配额 | 2048(2GB) | 邮箱容量,单位 MB,2048 = 2GB |
创建后邮箱地址为 zhangsan@example.com。
11.3 使用 Webmail
为什么是这个地址: SOGo 是 Mailcow 自带的 Webmail 网页邮箱程序,可以直接在浏览器里收发邮件,不用装任何软件。
https://mail.example.com/SOGo
用 zhangsan@example.com + 密码登录。和正常邮箱一样,可以收件、发件、管理文件夹。
12. 配置 SPF / DKIM / DMARC / PTR(DNS 补全)
第2章已经配置了 A、CNAME(autodiscover/autoconfig)、MX 四条记录。现在配置 SPF、DKIM、DMARC、PTR 这四项认证记录。
为什么这些记录决定了你的邮件能否送达:
当 Gmail、QQ邮箱、Outlook 等收到你发的邮件后,会自动进行以下三项检查:
| 检查项 | 目的 | 不配置的后果 |
|---|---|---|
| SPF | 确认发件服务器是否被授权 | 邮件直接进垃圾箱或被拒收 |
| DKIM | 验证邮件数字签名是否被篡改 | 邮件被标记为「可疑邮件」 |
| DMARC | 告诉对方 SPF/DKIM 验证失败怎么处理 | 收件方按默认策略处理,通常丢弃 |
| PTR | IP 反向验证是否匹配域名 | Gmail 等会降低信誉分 |
缺任何一个,邮件都可能无法送达。 这是企业邮件服务器的必配项。
12.1 SPF 记录
做什么用的: SPF 告诉全世界「只有 mail.example.com 这台服务器有权发送 example.com 的邮件」。防止别人伪造你的域名发诈骗邮件。
记录类型:TXT 主机记录:@ 记录值:v=spf1 mx a -allTTL:10分钟(或默认)
-all表示拒绝非授权服务器发信(严格模式)。如果担心误拦截,可以先改用~all(仅标记不拒绝),运行稳定后再改回-all。
12.2 DKIM 记录
做什么用的: DKIM 给每封发出的邮件盖上一个数字签名。收件方用你 DNS 里的公钥验证签名,确认邮件确实是你发的、没有被篡改。
从 Mailcow 面板获取 DKIM 值:
登录
https://mail.example.com配置 → 反垃圾邮件 → DKIM → 选择域名
example.com点击 添加 或 生成密钥对
复制「DNS 记录」框里的完整 TXT 值(以
v=DKIM1; k=rsa; p=开头)
或者命令行获取:
cat /opt/mailcow-dockerized/data/conf/rspamd/dkim/example.com.txt
添加到域名管理:
记录类型:TXT 主机记录:dkim._domainkey 记录值:v=DKIM1; k=rsa; p=...(粘贴面板复制的内容) TTL:10分钟(或默认)
12.3 DMARC 记录
做什么用的: DMARC 告诉收件方:如果 SPF 或 DKIM 验证失败了,该怎么处理这封邮件——是放行、进垃圾箱、还是直接拒绝。同时还能收到验证失败的统计报告,方便排查谁在伪造你的域名。
记录类型:TXT 主机记录:_dmarc 记录值:v=DMARC1; p=reject; rua=mailto:mailauth-reports@example.com TTL:10分钟(或默认)
官方推荐
p=reject(验证失败直接拒绝)。初期可以先设为p=none(仅收集报告不处理),等确认 SPF/DKIM 配置正确后再逐步改为p=quarantine(进垃圾箱)或p=reject(拒绝)。
12.4 设置 PTR 反向解析
做什么用的: PTR 是反向 DNS 查询——根据你的服务器 IP 反查域名。Gmail、QQ、Outlook 等大邮箱会对比:邮件声称自己是 mail.example.com 发的,那这个 IP 的 PTR 必须是 mail.example.com。对不上就降低信誉分。
PTR 记录 不是在域名管理设置,而是需要在云服务器提供商处设置。
| 云厂商 | 操作方式 |
|---|---|
| 阿里云 | 工单 / ECS → 实例 → 更多 → 设置反向解析 |
| 腾讯云 | 云服务器 → 实例 → 更多 → 设置反向DNS |
| 华为云 | 弹性IP → 更多 → 设置反向解析 |
| AWS | EC2 → 弹性IP → 操作 → 反向DNS |
| 其他 | 提工单给云厂商,「申请设置反向解析 PTR 为 mail.example.com」 |
PTR 内容:**
mail.example.com**
12.5 验证 DNS 配置
# SPFdig TXT example.com +short# DKIMdig TXT dkim._domainkey.example.com +short# DMARCdig TXT _dmarc.example.com +short# PTRdig -x <你的服务器IP> +short
或使用在线工具:MXToolbox SuperTool
13. 修复容器内部 DNS 解析
13.1 问题现象
部署完成后你可能发现:
HTTPS 面板上证书始终是「不安全」
acme-mailcow 容器日志报错
No A or AAAA record found for hostname无法接收外部邮件
postfix 日志报错
bad command startup
根本原因: 容器内部的 DNS 解析链路断了。容器 DNS → Unbound 容器 → 外部 DNS 查询失败。因为 Mailcow 默认的 Unbound 没有配置上游转发器。
13.2 配置 Unbound 转发器
cd /opt/mailcow-dockerized
编辑 data/conf/unbound/unbound.conf,在文件末尾添加以下内容:
cat >> data/conf/unbound/unbound.conf << 'EOF'forward-zone: name: "." forward-addr: 8.8.8.8 forward-addr: 1.1.1.1 EOF
💡 解释: 这告诉 Unbound —— 当它要查询外部域名时,向 8.8.8.8(Google DNS)和 1.1.1.1(Cloudflare DNS)转发请求。不配置这行,Unbound 去问根服务器,在部分云环境会超时。
13.3 重启 Unbound 并验证
docker compose restart unbound-mailcowsleep 3
验证容器 DNS 是否修复:
# 从宿主机测试 Unbound 解析dig A mail.example.com @172.22.1.254 +short# 从 acme 容器内部测试docker compose exec -T acme-mailcow dig A mail.example.com +short
如果两条命令都返回你的服务器 IP,说明 DNS 修复成功。
14. 修复 HTTPS 证书(certbot 方案)
14.1 问题说明
Mailcow 的 acme-mailcow 容器会在容器内部自动向 Let's Encrypt 申请证书,但它依赖容器 DNS。如果第13章 DNS 没有修复,acme 容器无法解析域名,证书申请会失败。
除了修复 DNS,你可能还需要手动申请证书并用 certbot 替代。方法如下:
14.2 在宿主机上用 certbot 申请证书
# 确保 nginx 容器正在监听 80 端口curl -I http://127.0.0.1# 用 certbot 申请证书(webroot 模式)certbot certonly --webroot \ -w /opt/mailcow-dockerized/data/web/ \ -d mail.example.com \ -d autodiscover.example.com \ -d autoconfig.example.com \ --non-interactive --agree-tos -m admin@example.com
💡 解释:
certonly:仅获取证书,不修改 nginx 配置
--webroot:通过在网站目录下放置验证文件来证明域名所有权
-w:指定 webroot 路径为 Mailcow 的网页根目录
-d:指定要包含在证书里的域名,这里同时覆盖 mail/autodiscover/autoconfig
14.3 将证书安装到 Mailcow
# 复制到 Mailcow SSL 目录cp /etc/letsencrypt/live/mail.example.com/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pemcp /etc/letsencrypt/live/mail.example.com/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/key.pemchmod 644 /opt/mailcow-dockerized/data/assets/ssl/cert.pemchmod 600 /opt/mailcow-dockerized/data/assets/ssl/key.pem
14.4 将证书复制到 acme 容器
# 找到 acme 容器 IDCID=$(docker ps -q -f name=acme)# 先用 readlink -f 解析真实路径(Let's Encrypt 证书是符号链接)REAL_CERT=$(readlink -f /etc/letsencrypt/live/mail.example.com/fullchain.pem) REAL_KEY=$(readlink -f /etc/letsencrypt/live/mail.example.com/privkey.pem)# 用 -L 参数复制到容器docker cp -L "REAL_CERT"CID:/var/lib/acme/cert.pem docker cp -L "REAL_KEY"CID:/var/lib/acme/key.pem docker exec $CID chmod 600 /var/lib/acme/key.pem
14.5 将证书复制到 postfix 容器
postfix 也需要使用正确的证书来加密 SMTP 连接。用 docker cp 方式:
PFX=$(docker ps -q -f name=postfix) docker cp -L "REAL_CERT"PFX:/etc/ssl/mail/cert.pem docker cp -L "REAL_KEY"PFX:/etc/ssl/mail/key.pem
14.6 重启相关容器
cd /opt/mailcow-dockerized docker compose restart nginx-mailcow docker compose restart postfix-mailcow docker compose restart dovecot-mailcow
14.7 验证证书
openssl x509 -in /opt/mailcow-dockerized/data/assets/ssl/cert.pem -noout -issuer -subject -dates
输出示例:
issuer=C = US, O = Let's Encrypt, CN = YE2 subject=CN = mail.example.com notBefore=Jun 27 05:41:17 2026 GMT notAfter=Sep 25 05:41:16 2026 GMT
14.8 设置自动续期
certbot 会自动创建定时任务续期证书。每次续期后需要重新安装到容器:
# 创建续期钩子脚本cat > /etc/letsencrypt/renewal-hooks/post/mailcow-copy.sh << 'EOF'#!/bin/bashCERT_DIR=/etc/letsencrypt/live/mail.example.com ACL_DIR=/opt/mailcow-dockerized/data/assets/sslcp -L "CERT_DIR/fullchain.pem" "ACL_DIR/cert.pem"cp -L "CERT_DIR/privkey.pem" "ACL_DIR/key.pem"chmod 644 "$ACL_DIR/cert.pem"chmod 600 "$ACL_DIR/key.pem"CID=$(docker ps -q -f name=acme) REAL_CERT=(readlink -f "CERT_DIR/fullchain.pem") REAL_KEY=(readlink -f "CERT_DIR/privkey.pem") docker cp -L "REAL_CERT"CID:/var/lib/acme/cert.pem docker cp -L "REAL_KEY"CID:/var/lib/acme/key.pem PFX=$(docker ps -q -f name=postfix) docker cp -L "REAL_CERT"PFX:/etc/ssl/mail/cert.pem docker cp -L "REAL_KEY"PFX:/etc/ssl/mail/key.pemcd /opt/mailcow-dockerized docker compose restart nginx-mailcow postfix-mailcow dovecot-mailcow EOFchmod +x /etc/letsencrypt/renewal-hooks/post/mailcow-copy.sh
15. 排查出站 25 端口是否被封
15.1 为什么发不出去邮件?
如果配置了 SPF/DKIM/DMARC/PTR,邮箱能收到信但发不出信,最常见的两个原因:
云服务商封锁了出站 TCP 25 端口(最常见)
服务器本身有防火墙拦截了出站 25 端口(系统防火墙如 iptables/ufw)
SMTP 邮件协议只使用 TCP 协议。云安全组中开 UDP 25 端口对发邮件没有任何帮助,因为邮件传输根本不使用 UDP。
15.2 判断出站 25 端口是否被封
方法一:用 nc 测试连接到外部邮件服务器
# 测试到 QQ 邮箱的 25 端口是否通echo -e "EHLO test\r\n" | nc -w 5 mx3.qq.com 25
如果返回
220 ... XMail Esmtp QQ Mail Server...= 25 端口正常如果卡住不动并超时 = 出站 25 端口被封
# 也可以测试 Gmail 和 SendGridecho -e "EHLO test\r\n" | nc -w 5 gmail-smtp-in.l.google.com 25echo -e "EHLO test\r\n" | nc -w 5 smtp.sendgrid.net 25
方法二:用 Bash 内置的 /dev/tcp 进行精确测试
# 连接到 QQ MX 的 25 端口timeout 10 bash -c 'exec 3<>/dev/tcp/mx3.qq.com/25 2>&1 && echo "CONNECTED" && read -t 5 banner <&3 && echo "Banner: $banner" || echo "FAILED"'
方法三:查看 postfix 日志
docker compose logs --tail=20 postfix-mailcow 2>&1 | grep -E "to=|deferred|sent|relay="
如果看到:
connect to mx3.qq.com[203.205.219.57]:25: Connection timed out
说明出站 25 端口被封。
方法四:检查邮件队列
docker compose exec -T postfix-mailcow mailq
队列为空 = 邮件已成功发出
队列有邮件 = 发信被阻塞
方法五:检查系统防火墙
iptables -L -n 2>/dev/null | grep 25 ufw status 2>/dev/null
15.3 判断出站 25 端口是「被封」还是「路由不通」
执行以下测试有助于判断阻塞发生在哪里:
# 1. 测试 587 端口(通常不受封禁)timeout 5 bash -c 'exec 3<>/dev/tcp/smtp.qq.com/587 2>&1 && echo "587 OK" || echo "587 FAILED"'# 2. 测试 25 端口timeout 10 bash -c 'exec 3<>/dev/tcp/mx3.qq.com/25 2>&1 && echo "25 OK" || echo "25 FAILED"'# 3. 用 IP 直接测(绕过 DNS)timeout 10 bash -c 'exec 3<>/dev/tcp/203.205.219.57/25 2>&1 && echo "DIRECT IP 25 OK" || echo "DIRECT IP 25 FAILED"'
结果判断:
| 587 端口 | 25 端口 | IP 直连 25 | 结论 |
|---|---|---|---|
| ✅ OK | ✅ OK | — | 25 端口畅通,查其他原因 |
| ✅ OK | ❌ 超时 | ❌ 超时 | 云厂商封锁了出站 25 端口,需配 SMTP 中继 |
| ✅ OK | ❌ 超时 | ✅ OK | DNS 解析问题,查容器 DNS(第13章) |
| ❌ 失败 | ❌ 失败 | ❌ 失败 | 系统防火墙拦截,检查 iptables/ufw |
587 端口通但 25 端口不通,基本可以确定是云服务商策略性封锁了出站 25,而不是服务器自身防火墙的问题。
15.4 解决方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| SMTP 中继(推荐) | 立即生效,无需联系云厂商 | 依赖第三方服务,免费额度有限 |
| 申请解封 25 端口 | 独立收发邮件,不依赖第三方 | 云厂商审核严格,通常需要企业资质 |
| 更换云服务器 | 选择不封 25 的厂商 | 迁移成本高 |
推荐方案:先配 SMTP 中继。用发件量小的免费额度先跑起来,后续再考虑解封 25 端口。
16. 配置 SMTP 中继(解决出站发信)
16.1 选择 SMTP 中继服务
| 服务商 | 免费额度 | 注册要求 | 推荐场景 |
|---|---|---|---|
| SendGrid | 100封/天 | 邮箱注册 | 个人/测试 |
| Mailgun | 100封/天 | 需要手机号 | 个人/测试 |
| Brevo (Sendinblue) | 300封/天 | 邮箱注册 | 个人/测试 |
| 腾讯云邮件推送 | 按量计费 | 实名认证 | 国内用户 |
16.2 以 SendGrid 为例配置中继
步骤 1:注册 SendGrid
打开 https://sendgrid.com/ 注册账号
邮箱验证后登录
左侧导航 → Settings → API Keys → Create API Key
步骤 2:获取 SMTP 凭据
API Key 类型选择 Full Access
复制生成的 API Key(只显示一次,保存好)
SendGrid 的 SMTP 配置:
服务器:
smtp.sendgrid.net端口:
587(TLS)用户名:
apikey(固定)密码:你刚才复制的 API Key
16.3 在 Mailcow 面板配置中继
方法 A:UI 面板配置(推荐)
面板右侧菜单 → 配置 → 邮件设置 → 中继 → 添加
| 字段 | 值 |
|---|---|
| 主机名和端口 | smtp.sendgrid.net:587 |
| 用户名 | apikey(SendGrid 固定) |
| 密码 | 粘贴 SendGrid API Key |
| 域名 | 留空(全局生效) |
如果「中继」菜单项未找到,请确认 Mailcow 版本为 2025-09 或更高。
方法 B:直接写入 MySQL(如果面板没有中继菜单)
cd /opt/mailcow-dockerized# 插入中继配置docker compose exec -T mysql-mailcow mysql -uroot -p$(grep -oP 'DBROOT=\K.*' mailcow.conf) mailcow -e "
INSERT INTO relayhosts (hostname, username, password, active)
VALUES ('smtp.sendgrid.net:587', 'apikey', '你的SendGrid_API_Key', 1);
"然后重启 postfix:
docker compose restart postfix-mailcow
16.4 验证发信
登录 Webmail
https://mail.example.com/SOGo用
zhangsan@example.com登录写一封邮件发给自己的 QQ 邮箱测试
检查邮件队列是否为空:
docker compose exec -T postfix-mailcow mailq
查看发送日志:
docker compose logs --tail=10 postfix-mailcow 2>&1 | grep -E "relay=|status=sent|to="
如果看到
relay=smtp.sendgrid.net和status=sent,说明配置成功。
16.5 解封 25 端口(可选)
如果后续希望不经过中继直接发信,需要向云厂商申请解封 25 端口:
申请模板: 【云厂商 25端口解封申请】- 服务器IP:[填写]- 域名:example.com- 用途:自建企业邮件服务器收发公司邮件- 已配置:SPF/DKIM/DMARC/PTR 均已配置完毕- 承诺:遵守《互联网电子邮件服务管理办法》,不发送垃圾邮件
⚠️ 注意:即使解封了 25 端口,SPF/DKIM/DMARC/PTR 也必须配置,否则邮件仍然无法送达。
17. 验证收发邮件
17.1 收件测试
从 QQ邮箱 或 Gmail 发送一封邮件到
zhangsan@example.com登录 Webmail
https://mail.example.com/SOGo检查收件箱是否收到
如果收不到,检查 postfix 日志:
docker compose logs --tail=30 postfix-mailcow 2>&1 | grep -E "from=|to=|Client host"
17.2 发件测试
在 Webmail 写一封邮件发送到你的 QQ 邮箱
检查 QQ邮箱的收件箱、垃圾箱
如果收不到,检查队列和日志:
# 查看队列docker compose exec -T postfix-mailcow mailq# 查看日志docker compose logs --tail=20 postfix-mailcow 2>&1 | grep -E "to=|deferred|sent|status="
17.3 常见发件问题排查
| 问题 | 日志特征 | 解决方法 |
|---|---|---|
| 出站 25 被封 | Connection timed out | 配 SMTP 中继(第15章) |
| SPF 未配置 | Gmail退回说未授权 | 添加 SPF 记录(第12章) |
| DKIM 未配置 | 邮件变「未签名」 | 添加 DKIM 记录(第12章) |
| IP 在黑名单 | blocked using zen.spamhaus.org | 检查 IP 信誉,联系云厂商 |
| PTR 未设置 | Gmail 标记为垃圾 | 设置 PTR(第12章) |
18. 日常运维
18.1 服务管理
# 查看所有容器状态docker compose ps# 停止所有服务docker compose stop# 启动所有服务docker compose start# 重启某个服务docker compose restart nginx-mailcow# 查看日志docker compose logs -f docker compose logs -f postfix-mailcow docker compose logs -f acme-mailcow
18.2 查看邮件队列
docker compose exec -T postfix-mailcow mailq
如果队列不为空,可以强制重试:
docker compose exec -T postfix-mailcow postqueue -f
18.3 备份
为什么做这步: 邮件数据(收件箱、发件箱、附件)保存在 Docker 数据卷里。如果服务器出问题,没有备份就等于所有邮件丢了。建议每周执行一次。
cd /opt/mailcow-dockerized docker compose down # 停止所有容器,保证数据一致性tar -czf /root/mailcow-backup-$(date +%Y%m%d).tar.gz /opt/mailcow-dockerized # 压缩整个目录docker compose up -d # 重新启动服务
备份文件保存在 /root/ 下,文件名包含日期,如 mailcow-backup-20250627.tar.gz。
注意: 生产环境建议用 backup.sh 脚本,它会正确排空并备份数据库。
18.4 升级
为什么做这步: Mailcow 持续更新安全补丁和新功能。每 1~3 个月升级一次可以修复已知漏洞。
cd /opt/mailcow-dockerized git pull docker compose pull # 下载最新版镜像docker compose up -d # 用新镜像重建容器./update.sh # 运行 Mailcow 自带更新脚本(更新数据库结构等)
18.5 重置管理员密码
什么时候用: 忘了 admin 密码,或者 moohoo 登录失败时用。
cd /opt/mailcow-dockerized
docker compose exec -T mysql-mailcow mysql -uroot -p$(grep -oP 'DBROOT=\K.*' mailcow.conf) -e \ "USE mailcow; REPLACE INTO admin (username, password, superadmin, active) VALUES ('admin', '\2y\10\$NLRS/FE07kqBWB5cGNjPOeKcxS9C8VTJPi8VQC8q5NQ.RWYqnPwK6', 1, 1);"执行后密码恢复为 moohoo。
18.6 完全重来
什么时候用: 部署过程中搞砸了,想从零开始重新部署,或者练习多次安装时用。
cd /opt/mailcow-dockerized docker compose down -v # 停止并删除所有容器和数据卷(注意:会丢失所有邮件数据!)rm -f mailcow.conf # 删除配置文件# 从第7步(./generate_config.sh)重新开始
⚠️
-v参数会删除所有邮件数据,生产环境千万不能用。仅限测试练习使用。
⚠️ 常见问题排错指南
Q1:登录时提示密码错误
原因: MySQL 容器初始化异常(通常是 Unbound 健康检查失败导致的)。
解决: 先尝试重置密码(第18.5节)。如果无效,检查 mailcow.conf 是否包含 SKIP_UNBOUND_HEALTHCHECK=y,没有则加上后重新部署。
Q2:HTTPS 证书不安全
原因: acme-mailcow 容器内部 DNS 解析失败。
排查:
docker compose logs acme-mailcow
如果看到 No A or AAAA record found,说明 DNS 问题。按第13章修复 DNS,然后按第14章手动安装 certbot 证书。
Q3:收不到外部邮件
排查步骤:
确认 DNS MX 记录生效:
nslookup -type=mx example.com确认端口 25 入站通:
telnet mail.example.com 25(从另一台电脑测试)检查 postfix 日志:
docker compose logs postfix-mailcow | grep -E "Client host|from="
Q4:发件对方收不到
排查步骤:
检查邮件队列:
docker compose exec -T postfix-mailcow mailq如果队列为空但对方没收到,可能是进了垃圾箱,检查 SPF/DKIM/DMARC(第12章)
如果队列有邮件且状态为
deferred:Connection timed out→ 出站 25 被封 → 配 SMTP 中继(第15章)blocked using zen.spamhaus.org→ IP 在黑名单 → 联系云厂商Helo command rejected→ 检查 myhostname 配置
Q5:acme 容器一直重启
原因: 配置文件有语法错误或 DNS 不通。
排查:
docker compose logs acme-mailcow
如果看到 unbound.conf 相关错误,检查第13章的 Unbound 配置是否正确。
Q6:容器内部 DNS 不通
排查:
# 测试 Unbounddig A mail.example.com @172.22.1.254 +short# 测试 acme 容器docker compose exec -T acme-mailcow dig A mail.example.com +short
如果宿主机正常但容器不通,执行:
docker compose restart unbound-mailcowsleep 3 docker compose exec -T acme-mailcow dig A mail.example.com +short
✅ 部署完成检查清单
基础配置
<input type="checkbox" disabled=""/>A 记录
mail.example.com→ 服务器 IP<input type="checkbox" disabled=""/>CNAME
autodiscover→mail.example.com.<input type="checkbox" disabled=""/>CNAME
autoconfig→mail.example.com.<input type="checkbox" disabled=""/>MX
@→mail.example.com.
部署流程
<input type="checkbox" disabled=""/>SSH 连上服务器,apt install 完成
<input type="checkbox" disabled=""/>Docker 已安装并运行
<input type="checkbox" disabled=""/>Mailcow 代码已克隆
<input type="checkbox" disabled=""/>
generate_config.sh执行完毕<input type="checkbox" disabled=""/>
docker compose pull完成<input type="checkbox" disabled=""/>
docker compose up -d所有容器 Up<input type="checkbox" disabled=""/>能打开
https://mail.example.com<input type="checkbox" disabled=""/>用
admin / moohoo登录成功(已改密码)<input type="checkbox" disabled=""/>添加了域名 example.com
<input type="checkbox" disabled=""/>创建了邮箱用户
zhangsan@example.com
DNS 认证(必配,否则发信失败)
<input type="checkbox" disabled=""/>SPF 记录
v=spf1 mx a -all<input type="checkbox" disabled=""/>DKIM 记录
dkim._domainkey<input type="checkbox" disabled=""/>DMARC 记录
_dmarc<input type="checkbox" disabled=""/>PTR 反向解析
mail.example.com
修复配置
<input type="checkbox" disabled=""/>Container DNS 已修复(Unbound 转发器)
<input type="checkbox" disabled=""/>HTTPS 证书已安装(Let's Encrypt / 自动)
<input type="checkbox" disabled=""/>SMTP 中继已配置(如出站 25 被封)
<input type="checkbox" disabled=""/>出站 25 端口已解封(可选)
功能验证
<input type="checkbox" disabled=""/>能收到外部邮件(如从 QQ 发送)
<input type="checkbox" disabled=""/>能发送邮件到外部邮箱(如 QQ 收到)
<input type="checkbox" disabled=""/>邮件不进垃圾箱(SPF/DKIM/DMARC/PTR 均生效)
参考链接:
祝你部署顺利!🐝🐮