Mailcow VPS自建邮箱:Gmail接受邮件的配置方法
安装mailcow前先测试出站TCP 25端口,否则邮件会滞留队列并在5天后退回;再配置PTR反向DNS,避免Gmail返回550 5.7.25拒收。
正在构建的内容
在您拥有的服务器上构建完整的邮件服务器:使用 SMTP 发送和接收邮件,使用 IMAP 让手机和笔记本电脑保持同步,提供 Webmail 客户端,并使用垃圾邮件过滤器对双向邮件的每条消息进行评分。mailcow-dockerized 将 Postfix、Dovecot、Rspamd、SOGo Webmail、MariaDB、Redis 和 ACME 客户端整合到一个 Docker Compose 堆栈中,因此软件本身并不是难点。您可以在半小时内完成部署并运行。
难点在于外围配置。对于电子邮件服务,互联网中的其他服务会主动不信任全新的服务器;从“可以正常工作”到“Gmail 静默丢弃所有邮件”,主要取决于4条 DNS 记录,以及1项您可能无法完全控制的 IP 信誉设置。租用服务器前,请先阅读下面的前置条件。如果读完后您认为建立信誉的过程不值得,那么这也是合理的选择。我们在2026年盘点的真正值得自行托管的服务正是基于这些原因,将邮件归入“只有确实有此需求时才自行托管”。
前置条件就是项目本身
任何一项缺失,您发送的邮件都可能无法送达。以下按这些问题导致失败的常见程度大致排序:
必须开放出站 TCP 25 端口。 您的服务器通过 TCP 25 端口向 Gmail 和 Microsoft 投递邮件。许多 VPS 和云服务商默认阻止出站 25 端口,以遏制垃圾邮件。此类阻止通常不会明确报错:系统启动时没有错误,服务看起来也都正常,但邮件会一直在队列中处于 deferred 状态,直到 Postfix 放弃并退回邮件(mailcow 将队列最长保留时间设置为 five days)。安装任何组件前先进行测试。如果端口被阻止,唯一的解决办法是提交支持工单,请服务商开放该端口;有些服务商会为已使用一段时间的账户开放,有些则永远不会开放。
使用声誉良好的干净 IP。 回收使用的 VPS IP 经常因前一位租户发送垃圾邮件而已被列入封禁名单。正式部署前,请通过 Spamhaus lookup 或 mxtoolbox 等服务检查 IP。IP 一旦被列入名单,您无法通过编写代码解决由此产生的拒收问题。
能够控制 DNS,并设置正确的 PTR 记录。 您需要向域名的 DNS 区域添加记录,还需要为服务器 IP 设置反向 DNS(PTR),使其指向邮件主机名。PTR 几乎从不在您的 DNS 控制面板中设置,它由 IP 的所有者管理,因此应在 VPS 服务商的控制面板中设置,或通过工单申请设置。
6 GiB RAM 和 2 vCPU 是较为宽裕的最低配置。 mailcow 官方给出的私有部署最低要求是 6 GiB RAM 加 1 GiB swap;当有少量用户依赖该服务时,建议使用 8 GiB RAM。低于约 2.5 GiB 时,generate_config.sh 会建议禁用 ClamAV 病毒扫描器,以免内核开始终止容器。mailcow 文档规定的磁盘最低要求是在存储任何邮件前为 20 GiB,因此初始配置应超过这一容量,并为邮箱增长预留空间。
使用 DNS 名称,而不是裸 IP。 选择类似 mail.example.com 的主机名。这个名称会同时作为您的 MAILCOW_HOSTNAME、TLS 证书主题、PTR 目标和 SMTP banner。所有位置都应保持一致。
第 1 步:确认出站端口 25 已开放
先执行此步骤。如果失败,后续操作都没有意义。在全新的 VPS 上,尝试与真实邮件服务器建立 SMTP 会话:
sudo apt update && sudo apt install -y netcat-openbsd
nc -vz -w 5 gmail-smtp-in.l.google.com 25正常结果会立即返回:
Connection to gmail-smtp-in.l.google.com (142.250.x.x) 25 port [tcp/smtp] succeeded!端口被阻断时,会完整等待 5 秒,然后失败:
nc: connect to gmail-smtp-in.l.google.com port 25 (tcp) timed out: Operation now in progress在没有配置自有出站防火墙规则的全新 VPS 上,此超时表示服务商阻断了连接。这是服务商侧的网络过滤,不是您的防火墙导致的,因此无法通过本地修改解决。提交工单:“请为我的 VPS(<IP>)启用出站 TCP 端口 25;我正在运行合法的邮件服务器。”在该命令返回 “succeeded” 之前,不要安装 mailcow。请注意,入站端口 25(其他服务器连接到您的服务器)属于独立路径,通常处于开放状态;服务商限制的通常是出站方向。
第 2 步,立即设置 DNS 记录
DNS 更改需要时间传播,因此应在安装前发布所有可以发布的记录。假设您的域名是 example.com,邮件主机是 mail.example.com,IP 地址是 10.0.0.10。请在 DNS 区域中创建:
mail.example.com. A 10.0.0.10
mail.example.com. AAAA 2001:db8::10 ; only if you have IPv6
example.com. MX 10 mail.example.com.
example.com. TXT "v=spf1 mx -all"
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:postmaster@example.com"SPF 记录表示“只有我的 MX 记录可以代表此域发送邮件,其他来源全部拒绝”。先将 DMARC 设置为 p=none,这样您可以查看报告,而不会退回自己的邮件;确认对齐后,再收紧为 p=quarantine,随后设置为 p=reject。这里有意缺少两条记录:DKIM 由 mailcow 在第 6 步为您生成,PTR 则需要现在在服务提供商的控制面板中设置。
将 10.0.0.10 的 PTR(反向 DNS)设置为 mail.example.com,其值必须与 MAILCOW_HOSTNAME 完全一致。这是大多数人最容易忘记的单条记录,而大型邮件服务商会据此拒收邮件。如果您的控制面板没有 rDNS 字段,请提交工单。
步骤 3:安装 Docker
mailcow 需要 Docker Engine 24.0 或更高版本,以及 Docker Compose 2.0 或更高版本。Compose 插件(docker compose)是最简单的方式,但 mailcow 也支持独立的 docker-compose 二进制文件。请使用 Docker 官方便捷脚本,不要使用 Ubuntu 的 docker.io 软件包,因为该软件包完全不包含 Compose 插件:
curl -fsSL https://get.docker.com | sudo sh
sudo docker compose version您应看到一行 Docker Compose version,其中显示 v2.x 或 v5.x。Compose v5 于 2025 年发布,在功能上与 v2 完全相同,二者都满足 mailcow 的要求。如果 docker compose version 输出 docker: 'compose' is not a docker command,说明 Docker Engine 已安装,但未安装 Compose 插件。请从 Docker 的软件源安装该插件,重新运行上面的脚本,或参阅我们的 Docker Compose 基础指南,该指南会从 Docker 自己的 apt 软件源同时安装二者。
第 4 步:克隆 mailcow 并生成配置
cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
umask
sudo ./generate_config.sh检查 umask 是否输出 0022,是因为 mailcow 自己的安装步骤会在克隆前设置 umask 0022。mailcow 不会检查该值,而全新的 Ubuntu 24.04 root shell 已经会提供 0022。随后,脚本会询问唯一重要的信息:完全限定主机名。输入 mail.example.com,该值必须与 A 记录和 PTR 记录完全一致。脚本会写入 mailcow.conf,这是整个堆栈读取的唯一环境文件。如需修改 Web 端口(HTTP_PORT、HTTPS_PORT)或在小型主机上禁用 ClamAV,请打开该文件:
MAILCOW_HOSTNAME=mail.example.com
HTTP_PORT=80
HTTPS_PORT=443
SKIP_CLAMD=n # set to y to drop the virus scanner on a <2.5 GiB box在低内存主机上,SKIP_FTS=y 是另一个可调整项:全文搜索是 mailcow 文档列出的第二个内存消耗大户。跳过它只会使 Webmail 无法搜索邮件正文。
除非主机上的其他程序已经占用这些端口,否则请保留 HTTP_PORT=80 和 HTTPS_PORT=443。mailcow 内置的 ACME 客户端默认使用 HTTP-01 challenge,获取证书时要求互联网可以访问端口 80。(mailcow 2026-03 及更高版本也可以通过 ACME_DNS_CHALLENGE=y 使用 DNS-01 challenge。该方式不需要端口 80,但需要访问 DNS 服务商的 API。)因此,简单的部署方式不会在同一台主机上运行独立的 nginx 与 Certbot 部署:mailcow 会在内部签发并续期自己的 TLS 证书,第二个服务占用 80/443 会导致冲突。mailcow 也记录了另一种方案:使用 HTTP_BIND=127.0.0.1 HTTP_PORT=8080 HTTPS_BIND=127.0.0.1 HTTPS_PORT=8443 将其 Web 监听器绑定到本地,在 80 和 443 上部署反向代理;如果希望由本地 Certbot 管理证书,则将 mailcow 指向这些证书,并添加续期钩子。对于邮件服务器而言,这会增加更多组件和配置。因此,任何需要公网 Web 前端的其他服务也存在同样的取舍:像将 Jellyfin 媒体库重新设计为 90 年代视频商店的 Halcyon这类服务需要在 443 上使用自己的反向代理,除非将 mailcow 放到该代理之后,否则应部署在另一台主机上。例外是完全不需要公网 Web 端口的服务:自托管的 RustDesk 中继只监听自己的 21115 到 21119 端口范围,因此只要有足够的带宽余量,就可以与 mailcow 共用这台主机。
第 5 步:启动堆栈并登录
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps拉取过程会获取大约两打镜像,请等待几分钟。当 docker compose ps 显示每个容器均为 running(或 healthy)时,在浏览器中打开 https://mail.example.com/admin。默认管理员登录信息为:用户名 admin,密码 moohoo。立即在管理界面中修改密码:进入 System → Configuration,然后进入 Access → Administrators。如果浏览器警告 NET::ERR_CERT_AUTHORITY_INVALID,说明 ACME 证书尚未签发。请先查看下面的 ACME 故障排查内容,不要直接认为配置已损坏;最初一两分钟使用自签名占位证书是正常现象。
第 6 步:添加域、邮箱并发布 DKIM
在管理界面中打开 E-Mail → Configuration,在 Domains 选项卡下点击 Add domain,然后输入 example.com。接着在 Mailboxes 下点击 Add mailbox,创建 you@example.com 并设置密码。此时,该邮箱已经可以通过 IMAP 访问。
现在配置 DKIM 密钥。进入 System → Configuration,然后选择 Options → ARC/DKIM keys。添加域后,mailcow 可能已经自动生成密钥;如果没有,请在此处生成密钥,选择该域,保留选择器 dkim,选择 2048-bit,然后点击 Add。复制页面显示的长 TXT 值,并将其发布为:
dkim._domainkey.example.com. TXT "v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqh...long-key...QAB"mailcow 的 Domains 页面有一个 DNS 按钮,其中会列出所需的每条记录,并用绿色勾或红色叉标记当前实际发布的状态。将它作为检查清单,在测试投递能力前确保每一行都显示绿色。发布后 DKIM 行仍显示红色,通常表示密钥被错误拆分成多个 TXT 片段。2048-bit 密钥超过单个 TXT 字符串的 255 字符限制,因此应将其作为一个逻辑值粘贴,并让 DNS 托管商自动为其拆分片段。
第 7 步:测试邮件送达率并争取 10/10
访问 mail-tester.com,复制页面显示的随机地址,然后从新邮箱向该地址发送邮件;登录 https://mail.example.com/SOGo 中的 SOGo Webmail,并从那里发送邮件。然后点击“Then check your score”。
目标是达到 10/10。常见扣分项及其原因如下:
- SPF 未对齐:缺少
MX/SPF 记录,或发送 IP 未包含在记录中。重新检查 SPF TXT 记录。 - DKIM 签名验证失败:缺少
dkim._domainkeyTXT 记录,记录仍在传播,或记录内容已损坏。这是最常见的问题。 - 没有 PTR / PTR 不匹配:反向 DNS 无法解析到
mail.example.com。联系服务提供商修复。 - 列入阻止列表:IP 之前的信誉导致该问题。申请移出阻止列表,或要求更换信誉更好的 IP。
在评分达到 10/10 之前,不要向 Gmail 或 Outlook 发送真实邮件。低评分加上新 IP,可能导致您的域名在启用当天就被标记。
第 8 步:连接实际的邮件客户端
使用以下设置,将 Thunderbird、Apple Mail 或手机连接到服务器。所有客户端使用的服务器主机名都是 mail.example.com:
- IMAP:端口 993,SSL/TLS(或使用 STARTTLS 的 143)
- SMTP 提交:端口 465,SSL/TLS(或使用 STARTTLS 的 587)
- 用户名:完整地址,
you@example.com - 密码:您设置的邮箱密码
不要通过端口 25 发送客户端邮件。该端口仅用于服务器之间的通信,mailcow 不在此端口提供经过身份验证的提交服务,客户端连接到该端口会被拒绝。如果客户端报告 Relay access denied,说明它正在尝试通过端口 25 发送邮件,或未进行身份验证;请将其切换到端口 465 或 587,并使用您的邮箱凭据。
第 9 步:备份真正重要的数据
mailcow 自带备份脚本,可为所有有状态卷创建快照。将备份写入外部磁盘或已挂载的远程存储:
sudo MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \
./helper-scripts/backup_and_restore.sh backup allall 会捕获 6 项数据。丢失其中任何一项都会导致数据丢失:vmail(实际的邮箱)、crypt(用于解密 vmail 的密钥;没有它就无法使用 vmail)、mysql(存储域名、用户、别名和设置的 MariaDB)、redis(队列和缓存状态)、rspamd(已学习的垃圾邮件和正常邮件特征)以及 postfix(邮件队列)。该脚本在辅助容器中运行,并写入压缩归档,因此即使整个服务栈正在运行,备份也能保持一致。使用每晚运行的 cron 任务自动执行,并添加 --delete-days 14 以清理旧备份集。停止工作的 cron 任务不会主动报告错误,因此应将脚本的退出状态发送到您确实能注意到的位置;自托管的 ntfy 服务器 可从同一个 cron 条目执行一条 curl 命令,并在故障发生当晚将通知发送到您的手机。使用 restore 运行同一个脚本即可恢复。该参数会列出快照,并让您选择要恢复的内容。未经测试恢复的备份只是希望,不是真正的备份。请先将其执行一次试运行,恢复到临时 VPS。
第 10 步:按计划更新
mailcow 通过自带脚本进行更新。该脚本会拉取新代码、迁移 mailcow.conf、预取镜像,并按顺序重启容器:
cd /opt/mailcow-dockerized
sudo ./update.sh --check # reports whether an update exists, changes nothing
sudo ./update.sh # applies it请先完成备份(第 9 步),因为数据库架构迁移很难回滚。更新发布频繁,并包含面向公网的守护进程所需的安全修复,因此不要让邮件服务器数月不更新。如果更新导致某个容器一直处于不健康状态,sudo docker compose logs --tail=50 <service>-mailcow 会指出未能恢复运行的守护进程。
加固说明
mailcow 会运行自己的 netfilter 服务(netfilter-mailcow),封禁反复攻击邮件和 webmail 端口的 IP,因此邮件服务默认已受到保护。但这不会保护主机自身的 SSH。SSH 仍然暴露在公网,并且仍会遭受暴力破解。请将此配置与 监控 SSH 认证日志的 Fail2ban 及仅使用密钥登录结合使用。mailcow 管理界面应使用强密码保护;理想情况下,不要将其暴露在公网,或将其置于 VPN 后。
故障模式及精确字符串
邮件进入队列但始终无法投递。 运行 sudo docker compose exec postfix-mailcow postqueue -p,或查看管理界面中的邮件队列;邮件会一直处于 deferred 状态,最多五天后 Postfix 才会将其退回,并显示:
status=deferred (connect to gmail-smtp-in.l.google.com[142.250.x.x]:25: Connection timed out)这几乎总是因为服务提供商阻止了出站端口 25(步骤 1)。主机自身防火墙中的出站规则也会产生相同的超时,因此应先排除该原因;如果主机配置正常,则无法通过配置修复,请提交工单。这不是 DNS 或 TLS 问题;判断依据是远程 MX 的端口 25 上出现 timed out。
Gmail 将所有邮件标记为垃圾邮件,或退回邮件。 在 Gmail 中打开邮件,选择“显示原始邮件”,然后查看身份验证结果。dkim=fail 或 dkim=none 表示您的 dkim._domainkey TXT 记录缺失、内容错误或尚未传播;请严格按照 ARC/DKIM 页面显示的内容重新发布,并等待 TTL 生效。spf=fail 表示 SPF/MX 记录未覆盖您的 IP。对齐状态至关重要;任何一项检查失败,都足以使邮件进入垃圾邮件。
连接时被大型服务提供商拒绝。 退信或 Postfix 日志中会包含 Gmail 的 PTR 拒绝信息:
550-5.7.25 [10.0.0.10] The IP address sending this message does not have a PTR
550-5.7.25 record setup, or the corresponding forward DNS entry does not match
550 5.7.25 the sending IP. As a policy, Gmail does not accept messages from IPs
550 5.7.25 with missing PTR records.550 5.7.25 代码表示反向 DNS 缺失或不匹配。请在服务提供商处将您 IP 的 PTR 设置为 mail.example.com(步骤 2)。正向 DNS(A)和反向 DNS(PTR)必须一致,并且都必须指向 mailcow 向其他服务器声明的同一主机名。
浏览器持续显示证书警告。 acme-mailcow 容器未能获取有效证书。检查其日志:
sudo docker compose logs acme-mailcow | tail -n 40如果看到类似 Cannot validate any hostnames, skipping Let's Encrypt for 1 hour. 的行或质询失败,表示互联网无法访问端口 80,或者 A 记录未指向此服务器。如果您发布了 AAAA 记录,也请检查该记录:mailcow 会优先根据 AAAA 记录进行验证;存在 AAAA 记录时会跳过 A 记录检查。因此,错误的 AAAA 记录,或无法访问端口 80 的 IPv6 路径,即使 A 记录正确,也会导致验证失败。确认 mail.example.com 解析到此主机,通过主机防火墙放行 80 和 443,并确保没有其他进程绑定这些端口。修复原因后,使用 sudo docker compose restart acme-mailcow 重启客户端,不要等待长达一小时的退避时间结束。
FAQ
自托管电子邮件真的值得吗?
如果您需要拥有数据、使用不限数量的别名并获得完全控制权,那么值得。mailcow 以一台 VPS 的价格提供专业的邮件服务栈。但可投递性需要持续维护:IP 信誉、DNS 对齐和阻止列表监控都不会真正结束。对于任务关键型业务地址,如果邮件进入某人的垃圾邮件文件夹一天就会造成损失,托管服务商是更务实的选择。只有在您更看重控制权而非便利性,并且确实愿意持续维护时,才应选择自托管。如果您真正想要的是让数据离开他人的服务器,而不一定是电子邮件,那么可以先部署一个不受他人控制的服务:照片库完全不存在可投递性问题,我们的 PhotoPrism 和 Immich 对比介绍了在同类 VPS 上部署所需的最低内存和备份命令。
如何确认出站端口 25 是否被阻止?
在服务器上运行 nc -vz -w 5 gmail-smtp-in.l.google.com 25。显示 “succeeded!” 表示端口已开放;暂停后显示 timed out 表示端口被阻止。如果服务器没有配置您自己的出站防火墙规则,那么阻止方就是服务商。这是自托管服务器可以接收邮件却始终无法发送邮件的最常见原因。服务商阻止端口时,唯一的解决方法是让服务商开放端口;修改本地设置无法解决。
为什么我的邮件仍会进入 Gmail 的垃圾邮件文件夹?
几乎总是因为身份验证链配置错误。在 Gmail 中使用“显示原始邮件”,查找 spf=pass、dkim=pass 和 dmarc=pass。dkim=fail 表明 dkim._domainkey TXT 记录缺失或配置错误;PTR 不匹配或使用没有发送历史记录的新 IP 也会降低投递率。先让 mail-tester.com 达到 10/10,然后逐步提高 IP 的发送量,每天先发送几封,再逐渐增加,而不是在第一天就发送大量邮件。
我具体需要备份哪些内容?
运行 backup_and_restore.sh backup all,并将完整备份集保存到服务器之外。该命令会备份 vmail(邮箱)、crypt(用于解密邮箱的密钥)、MariaDB 数据库(域名、用户、别名和设置)、Redis、Rspamd 的学习数据以及 Postfix 队列。crypt 卷最容易被忽略;没有它,vmail 备份只能得到无法读取的密文。至少在一次临时服务器上测试恢复。
我可以在 2 GB VPS 上运行 mailcow 吗?
运行得不会很稳定。generate_config.sh 会在内存低于约 2.5 GiB 时建议禁用 ClamAV。即使关闭 ClamAV,Rspamd、Dovecot、MariaDB 及其余服务也会争用内存,因此只要有实际负载,就会使用 swap,并可能触发 OOM kill。对于稳定的单用户安装,应将 6 GiB 内存加 1 GiB swap 视为最低配置;一旦依赖该服务的用户超过几人,就应立即升级到 8 GiB。