基于 Chasquid 配置邮件发送服务器
在开发 Web 应用时,经常会遇到发送验证码、密码重置邮件或者系统通知邮件的需求。最开始我考虑过直接部署 Mailcow,但研究之后发现,对于一个只需要发送邮件的场景来说,Mailcow 显得有些过于庞大了。
Mailcow 集成了 SMTP、IMAP、POP3、WebMail、反垃圾邮件、病毒扫描等完整邮件系统所需的组件,部署后会运行十多个 Docker 容器。对于企业邮箱服务而言这是合理的设计,但对于一个只需要发送验证码的应用来说,大量资源都处于闲置状态。
因此我开始寻找更加轻量的替代方案,最终选择了 Chasquid。
为什么选择 Chasquid
Chasquid 是一个使用 Go 编写的轻量级邮件服务器。与 Postfix、Exim 等传统邮件服务器相比,它的配置更加简单;与 Mailcow、iRedMail 等完整解决方案相比,它又足够轻量。
对于验证码和系统通知邮件,通常只需要这些能力:
- SMTP Submission(587)
- SMTPS(465)
- SMTP 服务器间投递(25)
- DKIM 邮件签名
- Let’s Encrypt TLS 证书
- SMTP 用户认证
这些能力 Chasquid 都可以覆盖。
本文部署环境如下:
1 | |
这里需要区分两个名字:
mail.example.com是邮件服务器主机名,用于 SMTP banner、TLS 证书、PTR 反向解析。example.com是发件域名,用于notify@example.com、SPF、DKIM、DMARC。
配置 DNS
在开始部署之前,先把 DNS 配好。
1 | |
如果你使用 Cloudflare,mail.example.com 这条 A 记录必须是 DNS only,不能开启代理。SMTP 不是 HTTP 服务,不能走 Cloudflare 的橙云代理。
如果云服务商支持 PTR(反向解析),建议将服务器 IP 反向解析回:
1 | |
这不是绝对必须,但很多收件方会把 PTR 是否存在、PTR 是否和 SMTP 主机名对齐作为信誉评分的一部分。
安装 Chasquid
Ubuntu 可以直接安装 Chasquid:
1 | |
不同安装方式创建的运行用户可能不同。Ubuntu 包通常会使用 chasquid 用户;如果你自己编译或手写 systemd service,也可能使用 mail 用户。
后面的命令用一个变量表示运行用户:
1 | |
如果你的服务实际使用 mail 用户,就改成:
1 | |
可以通过下面的命令确认:
1 | |
写入主配置
Chasquid 的配置文件是 protobuf text 格式,语法是:
1 | |
不是 key = value。
编辑 /etc/chasquid/chasquid.conf:
1 | |
写入:
1 | |
这几个字段分别表示:
hostname:SMTP banner、邮件头和默认证书选择会用到。smtp_address:服务器到服务器投递端口,通常是 25。submission_address:应用程序提交邮件端口,通常是 587,配合 STARTTLS。submission_over_tls_address:隐式 TLS 提交端口,通常是 465。
配置写完后,可以先检查解析结果:
1 | |
如果配置语法错了,这一步会直接报错。
准备数据目录
Chasquid 默认使用 /var/lib/chasquid 保存队列、缓存和本地 RPC socket。目录需要让运行用户可写:
1 | |
如果这个目录权限不对,启动时可能会看到:
1 | |
创建邮件域和用户
Chasquid 默认将域相关配置保存在 /etc/chasquid/domains 目录下。
1 | |
创建 SMTP 用户:
1 | |
这个账号就是后续应用程序连接 SMTP 时使用的账号。
如果想非交互式创建,也可以直接传入密码:
1 | |
可以用下面的命令验证账号密码是否正确:
1 | |
配置 DKIM
现代邮件系统几乎离不开 DKIM。
很多人第一次接触 DKIM 时容易产生一个误解:认为它需要类似 HTTPS 那样的第三方证书机构签发。实际上并不是。DKIM 本质上是邮件服务器使用私钥对邮件内容签名,收件方从 DNS 中获取公钥进行验证。
Chasquid 约定 DKIM 私钥文件名格式为:
1 | |
比如 selector 使用 20260619,文件就是:
1 | |
生成 DKIM key:
1 | |
生成 DNS TXT 记录:
1 | |
输出会类似:
1 | |
把它添加到 DNS。注意 TXT 记录名里的 20260619 就是 selector;如果你的 selector 是 mail,才会是 mail._domainkey.example.com。
配置 TLS
DKIM 解决的是邮件真实性问题,而 TLS 解决的是传输安全问题。
推荐使用 Let’s Encrypt 给 mail.example.com 签发证书。
如果服务器的 80 端口空闲,可以用 standalone:
1 | |
如果 80 端口已经被 Nginx 占用,或者你使用 Cloudflare,也可以使用 DNS 插件签发:
1 | |
1 | |
证书签好后通常会在:
1 | |
Chasquid 会从 /etc/chasquid/certs 读取证书目录。创建软链接:
1 | |
还需要确保 Chasquid 运行用户能读取 Let’s Encrypt 的私钥。用 ACL 比直接 chown /etc/letsencrypt 更合适:
1 | |
可以验证一下:
1 | |
输出 0 说明可读。
建议再加一个 certbot 续期后的重启钩子:
1 | |
处理 Systemd Socket Activation
有些发行版的 Chasquid 包会启用 systemd socket activation。此时配置里可能会看到类似:
1 | |
如果对应 socket 没启用,服务可能会报:
1 | |
对于只做验证码和系统通知发送的场景,我更倾向于直接让 Chasquid 自己监听端口,也就是前面配置里的:
1 | |
如果系统里存在 chasquid.socket,可以禁用它:
1 | |
然后启动 Chasquid:
1 | |
查看状态:
1 | |
正常情况下会看到类似:
1 | |
再检查端口:
1 | |
测试 SMTP 提交
应用程序建议使用 587 + STARTTLS:
1 | |
也可以使用 465 + TLS:
1 | |
使用 swaks 测试:
1 | |
1 | |
提交成功不等于远端邮箱一定已经收下。还要看 Chasquid 队列和日志:
1 | |
如果投递成功,会看到类似:
1 | |
队列应为空:
1 | |
检查认证结果
进一步可以将测试邮件发送到 Gmail,然后查看原始邮件头中的:
1 | |
理想情况下应该看到:
1 | |
如果邮件被收件方接受,但进入垃圾箱,通常要继续检查:
- PTR 是否指向
mail.example.com mail.example.com是否正向解析回同一个 IP- SPF、DKIM、DMARC 是否都通过
- 服务器 IP 是否在常见黑名单里
- 邮件内容是否过于像营销或垃圾邮件
- 验证码发送是否有频控,避免短时间大量重复邮件
关于 25 端口
Chasquid 作为邮件服务器直接投递到 Gmail、QQ、163 等收件方时,需要能从服务器连接对方 MX 的 TCP 25 端口。
可以测试:
1 | |
如果这里超时或被拒绝,说明你的 VPS、机房上游或网络策略可能限制了 outbound 25。此时应用仍然可以通过 587 把邮件提交给 Chasquid,但 Chasquid 可能无法继续投递到外部邮箱。
这种情况下有两个选择:
- 向 VPS 服务商申请放行 outbound 25。
- 使用外部 SMTP relay/smarthost,通过 587、465 或 2525 转发。
总结
对于只需要发送验证码和通知邮件的场景而言,Chasquid 是一个非常有吸引力的选择。
相比 Mailcow,它没有庞大的容器集群和复杂的邮件生态;相比 Postfix,它又拥有更现代、更容易理解的配置方式。整个部署过程中最容易踩坑的地方主要有几个:
chasquid.conf使用key: value,不是key = value。- 监听端口字段是
smtp_address、submission_address、submission_over_tls_address。 - DKIM 文件名要使用
dkim:SELECTOR.pem,DNS 记录名也要使用同一个 selector。 - Let’s Encrypt 私钥必须让 Chasquid 运行用户可读。
/var/lib/chasquid必须让 Chasquid 运行用户可写。- DNS、PTR、SPF、DKIM、DMARC 都要对齐。
- 直接投递外部邮箱时,outbound 25 必须可用。
完成这些配置后,一个轻量级邮件发送服务器就具备了生产环境所需的核心能力。对于个人项目、小型 SaaS 服务以及内部系统,这样的方案通常已经足够。