SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-28

mailcow VPS部署:让Gmail接受自托管邮件

安装 mailcow 前先测试出站 TCP 25 端口;端口被封锁会让邮件一直停在队列中。再配置 PTR,避免 Gmail 返回 550 5.7.25 拒收。

您要构建的内容

在您拥有的服务器上部署完整的邮件服务器:使用 SMTP 发送和接收邮件,使用 IMAP 让手机和笔记本电脑保持同步,提供 Webmail 客户端,并使用垃圾邮件过滤器对收发的每封邮件进行评分。mailcow-dockerized 将 Postfix、Dovecot、Rspamd、SOGo Webmail、MariaDB、Redis 和 ACME 客户端打包到一个 Docker Compose 堆栈中,因此软件本身并不是难点。您可以在半小时内让它运行起来。

真正困难的是周边配置。对于电子邮件服务,互联网中的其他服务会主动不信任全新的服务器。从“服务能正常运行”到“Gmail 悄悄吞掉每封邮件”,差别取决于4条 DNS 记录,以及一个您可能无法完全控制的 IP 信誉设置。在租用服务器之前,请先阅读下面的前提条件。如果读完后您认为建立信誉的过程不值得,那么这也是合理的结论;我们的2026年真正值得自行托管的服务概览正是基于这些原因,将电子邮件归入“只有确实需要时才自行托管”一类。

前置条件就是项目本身

其中任何一项缺失,您发送的邮件都可能无法到达。以下大致按最常导致问题的顺序排列:

出站 TCP 25 端口必须开放。 您的服务器通过 TCP 25 端口向 Gmail 和 Microsoft 交付邮件。许多 VPS 和云服务商默认封锁出站 25 端口,以减少垃圾邮件。此类封锁通常不会显示错误:系统启动时没有异常,服务看起来也正常,但邮件会一直停留在队列中。安装任何软件前先测试该端口。如果端口被封锁,唯一的解决方法是提交支持工单,请服务商解除封锁;有些服务商会为使用时间较长的账户解除封锁,有些则始终不会解除。

干净且信誉可用的 IP 地址。 回收使用的 VPS IP 地址通常因前一个租户发送垃圾邮件而已被列入阻止列表。在确定使用前,请通过 Spamhaus lookup 或 mxtoolbox 等服务检查您的 IP。IP 一旦被列入阻止列表,就会遭到拒收,无法通过编写代码解决。

DNS 管理权限以及正确的 PTR 记录。 您需要向域名的区域添加记录,还需要为服务器 IP 配置反向 DNS(PTR),使其指向您的邮件主机名。PTR 几乎从不在您的 DNS 控制台中设置,而是由 IP 的所有者管理。因此,您需要在 VPS 服务商的控制面板中设置,或提交支持工单。

6 GiB RAM 和 2 vCPU 是较为舒适的最低配置。 mailcow 对私有部署的最低要求是 6 GiB RAM 加 1 GiB swap;当有少量用户依赖该服务后,建议使用 8 GiB。内存低于约 2.5 GiB 时,generate_config.sh 会建议禁用 ClamAV 病毒扫描器,以免内核开始终止容器。初始分配 20 GB SSD。

使用 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(IP 为 <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 需要带有 Compose v2 插件的 Docker Engine。请使用 Docker 的官方便捷脚本,不要使用 Ubuntu 的 docker.io 软件包,因为其中完全不包含 Compose 插件:

curl -fsSL https://get.docker.com | sudo sh
sudo docker compose version

您应看到一行 Docker Compose version v2.x。如果 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 使用异常的文件掩码时会拒绝构建,而全新的 Ubuntu 24.04 root shell 已经会提供 0022。随后,脚本会询问唯一重要的信息:完全限定主机名。输入 mail.example.com。该值必须与 A 记录和 PTR 记录完全一致。脚本会写入 mailcow.conf,这是整个堆栈读取的唯一环境文件。如果需要修改 Web 端口(HTTP_PORTHTTPS_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=80HTTPS_PORT=443,否则不要修改它们。mailcow 内置的 ACME 客户端需要从互联网访问端口 80 才能获取证书。因此,不要在同一台服务器上运行 独立的 nginx 加 Certbot 配置。mailcow 会在内部签发并续期自己的 TLS 证书,第二个服务占用 80/443 会导致冲突。任何需要公网 Web 前端的其他服务也一样。因此,像 将 Jellyfin 媒体库重新设计成 90 年代录像店的 Halcyon 这类要求在 443 上使用自己的反向代理的服务,应部署在另一台主机上。例外是完全不需要公网 Web 端口的服务:自托管的 RustDesk 中继 仅监听 21115 到 21119 端口范围。只要还有足够的带宽余量,它就可以与 mailcow 共用这台主机。

第 5 步:启动堆栈并登录

sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps

拉取过程会获取大约 20 多个镜像,请等待几分钟。当 docker compose ps 显示每个容器均为 running(或 healthy)时,在浏览器中打开 https://mail.example.com。默认管理员登录凭据为:用户名 admin,密码 moohoo。立即在管理界面的 Access → Administrators 中修改该密码。如果浏览器警告 NET::ERR_CERT_AUTHORITY_INVALID,说明 ACME 证书尚未签发。请先参阅下面的 ACME failure,再判断服务是否出现故障;开始的 1 到 2 分钟使用自签名占位证书是正常现象。

第 6 步,添加域名、邮箱并发布 DKIM

在管理界面中打开 Mail Setup 页面(Configuration → Mail Setup),在 Domains 选项卡下点击 Add domain,然后输入 example.com。接着在 Mailboxes 下点击 Add mailbox,使用密码创建 you@example.com。此邮箱已可正常使用,并可通过 IMAP 访问。

现在配置 DKIM 密钥。转到 Configuration → 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._domainkey TXT 记录,记录仍在传播,或记录内容已损坏。这是最常见的问题。
  • 没有 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 all

all 会捕获 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 状态停留,并显示:

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=faildkim=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)。正向解析(A)和反向解析(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 记录未指向此服务器。确认 mail.example.com 解析到此服务器,允许主机防火墙放行 80 和 443,并确保没有其他程序占用这些端口。修复原因后,使用 sudo docker compose restart acme-mailcow 重启客户端,不要等待长达 1 小时的退避时间结束。

FAQ

自托管电子邮件真的值得吗?

如果您重视数据所有权、无限别名和完全控制权,值得。mailcow 只需一台 VPS 的费用,就能提供专业的邮件服务栈。但邮件送达率需要持续维护:IP 信誉、DNS 对齐和阻止列表监控都没有终点。如果这是关键业务邮箱,而进入他人垃圾邮件文件夹一天就会造成损失,托管服务商是更务实的选择。只有在您更重视控制权而非便利性,并且确实会持续维护时,才适合自托管。如果您真正想要的是让数据脱离他人的服务器,而不一定是电子邮件,建议先部署一个不受他人影响的服务:照片库完全没有邮件送达率问题;我们的 PhotoPrism 和 Immich 对比介绍了在同类 VPS 上部署这类服务所需的最低 RAM 和备份命令。

如何确认出站端口 25 是否被阻止?

在服务器上运行 nc -vz -w 5 gmail-smtp-in.l.google.com 25。显示“succeeded!”表示端口已开放;暂停后显示 timed out,表示服务商阻止了该端口。这是自托管服务器能接收邮件却始终无法发送邮件的最常见原因。唯一的解决方法是让服务商开放端口,本地设置无法改变这一点。

为什么我的邮件仍会进入 Gmail 的垃圾邮件文件夹?

几乎总是因为身份验证链配置错误。在 Gmail 中使用“显示原始邮件”,查找 spf=passdkim=passdmarc=passdkim=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 的选项。即使禁用,Rspamd、ClamAV、Dovecot 和 MariaDB 仍会争用内存,因此只要有实际负载,就可能触发 swap 和 OOM kill。对于稳定的单用户安装,应将 6 GiB 加 1 GiB swap 视为最低配置;一旦依赖该服务的用户超过两人,就应升级到 8 GiB。