给 Stalwart 邮件服务器挂上阿里云邮件推送 SMTP 中继
问题背景
服务器 192.168.x.x 上跑了 Stalwart Mail Server v0.16.19,发信域 vtdd.vip。发件出口是电信家宽 IP 14.19.x.x,没有 PTR 反向解析。
发往 Gmail 时被退信:
550-5.7.25 The IP address sending this message does not have a PTR record setup550-5.7.25 or the corresponding forward DNS entry does not point to the sending IP.PTR 只能由 IP 所有者设置,家宽 IP 无法自己做,只能改用 SMTP 中继(smarthost)发信。
方案选择
- 阿里云邮件推送
smtpdm.aliyun.com:465:个人实名可用,域名验证、发信地址、SMTP 密码、SPF/DKIM/DMARC 一条龙;本次实测成功。 - 腾讯云 SES 理论上可行,但 2026-03-02 起新开通的个人实名账号不再支持 SMTP 发信,只能 API/控制台,不适合直接给 Stalwart 当 smarthost。
前置准备:阿里云控制台
- 开通 邮件推送(https://dm.console.aliyun.com)。
- 发信域名 → 新建
vtdd.vip,按提示在 DNS 加:- 域名所有权 TXT
- SPF TXT:
v=spf1 a mx include:spf1.dm.aliyun.com ip4:14.19.x.x ~all - DKIM TXT:
aliyun-cn-hangzhou._domainkey - DMARC TXT:
_dmarc.vtdd.vip→v=DMARC1; p=none; rua=mailto:dmarc_report@service.aliyun.com
- 回阿里云点验证,等 DNS 生效。
- 发信地址 → 新建
zero@vtdd.vip,发信类型务必选 「触发邮件」。 - 给该地址设置 SMTP 密码(不是阿里云登录密码,是单独的 SMTP 密码)。
注意:一个域名只能有一条 SPF TXT,已有记录要合并而不是新增第二条。
Stalwart 侧配置
1. 建 Relay 路由
路径:Settings → MTA → Outbound → Routes → Create
| 字段 | 值 |
|---|---|
| Name | aliyun |
| Route type | Relay Host |
| Address | smtpdm.aliyun.com |
| Port | 465 |
| Protocol | SMTP |
| Implicit TLS | 开 |
| Allow Invalid Certs | 关 |
| Auth Username | zero@vtdd.vip |
| Auth Secret → Secret value | 阿里云 SMTP 密码 |
2. 让非本地邮件走这个路由
路径:Settings → MTA → Outbound → Strategy
把 route 表达式的 ELSE 从 'mx' 改为 'aliyun':
if is_local_domain(rcpt_domain) → 'local'else → 'aliyun' ← 改这一处必须带单引号。
3. 关闭 DANE / MTA-STS
路径:Settings → MTA → Outbound → TLS Strategies
| 字段 | 值 |
|---|---|
| DANE | disable |
| MTA-STS | disable |
走中继后,收件方策略不再适用于中继连接,留着会失败。
4. 保存并重启
右上角 Save & Reload 后,必须重启容器:
docker restart stalwart坑点:Stalwart 的 Strategy(单例策略)改完后光 Save / Reload 不会热加载,不重启就仍然走直连
mx。这是本次最大的坑。
验证中继是否真的生效
Stalwart 容器默认不往 stdout 写日志(docker logs 是空的),/api/* 管理接口也查不了队列,只能抓网络层。
方法:监听 /proc/net/tcp 的 465 出站连接
上传到服务器后台跑:
nohup bash -c 'for i in $(seq 1 300); do docker exec stalwart cat /proc/net/tcp | \ awk "NR>1{n=split(\$3,a,\":\"); if(n==2 && a[2]==\"01D1\") print systime()\" \"a[1]\" \"\$4}" sleep 1done' >/dev/null 2>&1 &然后立刻发一封测试邮件。查看结果:
cat /tmp/relay_watch.logrem_address是106.11.x.x(对应smtpdm.aliyun.com)→ 走中继成功。rem_address是14.19.x.x或没有记录 → 仍直连,检查 Strategy ELSE 是否保存并重启。
注意:一定要用
$3(远端地址)匹配01D1,不能全行grep ':01D1',否则会误命中本机 465 的 LISTEN 条目。
认证结果与垃圾箱问题
Gmail 收到的原始邮件头:
Authentication-Results: mx.google.com; spf=pass (google.com: domain of zero@vtdd.vip designates 47.90.196.146 as permitted sender) dkim=pass header.i=@vtdd.vip header.s=aliyun-cn-hangzhou header.b=... dkim=neutral (invalid public key) header.i=@vtdd.vip header.s=aliyun-cn-hangzhou-cn ... dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=vtdd.vip- SPF pass:阿里云 IP 被授权。
- DKIM pass:
aliyun-cn-hangzhou这条签名通过了。 dkim=neutral (invalid public key)对应另一个 selectoraliyun-cn-hangzhou-cn——该 selector 没有 DNS 记录,是中性结果,不影响 DMARC。- DMARC pass:因为
p=none,SPF 对齐通过即可。
如果邮件仍进垃圾箱,通常是阿里云共享 IP 信誉低、域名太新、没有历史发信记录导致的 Gmail 内部评分问题。处理办法:
- 多发几封并正常打开/回复,建立信誉。
- 在 Gmail 里把这封信标为「非垃圾」,把
zero@vtdd.vip加入联系人。 - 持续进垃圾箱再考虑换其他中继服务。
关键坑点汇总
- Strategy ELSE 带单引号:
'aliyun'不能写成aliyun。 - 改完 Strategy 必须重启容器,Save & Reload 不够。
- 路由名找不到时 Stalwart 静默回退直连,不会报错——表现就是「阿里云没记录、Gmail 能收到但进垃圾箱」。
- 一个域名只能有一条 SPF TXT,合并而不是叠加。
- DMARC 先保持
p=none,观察稳定后再收紧,否则 DKIM 不对齐时会被直接拒收。
参考命令
容器内 465 连通性测试:
docker exec stalwart bash -c 'printf "EHLO mail.vtdd.vip\r\nQUIT\r\n" | timeout 15 openssl s_client -connect smtpdm.aliyun.com:465 -servername smtpdm.aliyun.com -crlf -quiet'重启并确认健康:
sudo docker restart stalwartsleep 25docker ps --filter name=stalwart文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!



