通过自托管,Listmonk 跑在你本就在付费的 VPS 上。发信成本就是你的 SMTP 中继按每千封邮件收取的费用。订阅者数量不会改变这两个数字中的任何一个。这正是结构性的转变,让自托管在你的需求超出托管版免费额度之后值得花那点搭建时间。
Listmonk 是一款基于 Go 的开源邮件通讯管理工具。只需一台 VPS 加一个 SMTP 中继账号的成本,你就能拥有无限的订阅者、列表和发信活动。在你敲第一条命令之前,有一点必须搞清楚:Listmonk 处理除了实际发信之外的一切。你的邮件落入收件箱还是垃圾邮件箱,取决于你配置的 SMTP 中继和你在发信域名上设置的 DNS 记录。
本指南涵盖什么
- 用 Docker Compose 在带 HTTPS 的 Nginx(或 Caddy)反向代理后面部署 Listmonk 和 PostgreSQL
- 根据你的发信量和预算选对 SMTP 中继(Amazon SES、Postmark、Brevo 或其他)
- 在你的发信域名上配置 SPF、DKIM 和 DMARC
- 避开四种上线后才暴露、且往往不会报出明确错误的故障模式
- 预计耗时:如果你已经备好 VPS 和域名,30 分钟。
- 不在范围内:滴灌自动化、事务性邮件、多实例部署(见 FAQ)
什么情况下 Listmonk 是错的工具
Listmonk 是某种特定场景下的正确答案。如果你的场景不同,就会有更好的答案。
每月发信量低于约 1 万封。 在这个规模下,Brevo 或 Mailchimp 的托管版免费额度,全算下来可能比一台 VPS 加一个 SMTP 中继更便宜。自托管要等你跨过这个区间才开始划算。部署之前,先拿你真实的订阅者数量和发信频率把账算一遍。
非技术团队。 对于不在终端里工作的人,Mailchimp 和 Brevo 的界面确实更好用。Listmonk 默认团队里有人能 SSH 进服务器、看懂 Docker 日志、判断 DNS 是否生效。如果没有这样的人,托管服务才是正确选择。
需要自动化工作流。 Listmonk 发送发信活动。它不支持滴灌序列、行为触发邮件,也没有可视化工作流编排器。如果你需要这些,就运行 Mautic 或者把 Listmonk 接到 n8n 上来承担自动化这一层。
受 GDPR 约束的订阅者列表。 如果你的订阅者主要在欧盟,或你的列表受 GDPR 数据驻留规则约束,就把 Listmonk 部署在欧洲数据中心。我们提供符合欧盟驻留要求的法兰克福和伦敦机房。
开始之前你需要准备什么
Listmonk 加 PostgreSQL 再加一份中等强度的队列负载,至少需要 2 GB RAM 打底。4 GB 是舒适的生产环境目标。
硬件。 对于每月发信量低于 5 万封的个人列表,一台配 2 vCPU、4 GB RAM 和 120 GB NVMe 存储的 VPS 就够用。每月 20 万封以上、还在增长的列表需要 4 vCPU 和 8 GB RAM。我们这套 Compose 部署跑在法兰克福一台 4 GB 的 VPS 上。如果可以,挑一个离你订阅者近的机房。发信延迟无所谓;管理后台的响应速度才重要。
域名。 一个通过 A 记录指向你 VPS 的域名。用一个子域名作管理界面,例如 mail.example.com。发信域名和管理子域名可以是同一个主域名。
SMTP 中继账号。 先别急着创建。中继的选择是本指南中最关键的决定,而它取决于你的发信量。先跳到“选择你的 SMTP 中继”那一节,挑一个服务商,再回到这里,手上备好 SMTP 主机、端口、用户名和密码。
VPS 上的软件。 Ubuntu 22.04 LTS 或 24.04 LTS。Docker Engine 24.0 或更高版本,并带 Docker Compose 插件。UFW 或同等防火墙,开放 22、80、443 端口。以非 root 的 sudo 用户身份获得 SSH 访问权限。
用 Docker Compose 部署 Listmonk

为这次部署建一个目录,然后放进一个 docker-compose.yml 文件,里面有两个服务:postgres 作数据库,listmonk 作应用。两者都在失败时重启。Listmonk 绑定到 127.0.0.1 因此反向代理是唯一能访问它的东西。
Docker Compose 文件
我需要您提供完整的英文文本来进行翻译。您发送的内容是"Here is the",这不是完整的句子。
请提供完整的英文短语或标签,我将立即为您翻译成简体中文。 docker-compose.yml。对照 Listmonk 官方安装文档核对确切的镜像标签和环境变量名。它们会随每次发布而更新。
# docker-compose.yml
services:
postgres:
image: postgres:16-alpine
container_name: listmonk-postgres
restart: unless-stopped
environment:
POSTGRES_USER: listmonk
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: listmonk
volumes:
- listmonk-postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U listmonk"]
interval: 10s
timeout: 5s
retries: 6
app:
image: listmonk/listmonk:latest
container_name: listmonk-app
restart: unless-stopped
# Bind to loopback only. The reverse proxy is the public entrypoint.
ports:
- "127.0.0.1:9000:9000"
depends_on:
postgres:
condition: service_healthy
environment:
LISTMONK_app__address: "0.0.0.0:9000"
LISTMONK_db__host: postgres
LISTMONK_db__port: 5432
LISTMONK_db__user: listmonk
LISTMONK_db__password: ${POSTGRES_PASSWORD}
LISTMONK_db__database: listmonk
volumes:
listmonk-postgres:
创建 .env 文件包含 POSTGRES_PASSWORD= 设为一段很长的随机字符串。然后启动整套服务,运行一次性的数据库安装:
# Pull images and start the database first
docker compose up -d postgres
# Run the install step (creates schema and the first admin user)
docker compose run --rm app ./listmonk --install --idempotent --yes
# Start the application
docker compose up -d
当前 --install 命令会提示输入管理员邮箱和密码。把它们保存下来。确认两个容器都在运行:
docker compose ps
预期输出:列出两个服务,状态都是 Up。postgres 那一行应显示 (healthy)。
当前 127.0.0.1:9000 这个绑定是有意为之。Listmonk 没有内置的认证限速器,也没有 IP 白名单。把 9000 端口暴露到公网,意味着地球上任何人都能撞到你的管理员登录页。反向代理的作用,就是让这个登录页只能通过 HTTPS 访问。
Nginx 反向代理与 SSL
从 Ubuntu 仓库安装 Nginx 和 Certbot。在 /etc/nginx/sites-available/listmonk 创建一个站点配置,带上 Listmonk 生成正确发信活动链接所需的代理头:
# /etc/nginx/sites-available/listmonk
server {
listen 80;
server_name mail.example.com;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Listmonk streams campaign progress over WebSocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
把它软链到 sites-enabled,测试配置,重载 Nginx,然后签发证书:
sudo ln -s /etc/nginx/sites-available/listmonk /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d mail.example.com
Certbot 会重写 server 块,让它用新证书监听 443,并加上一条 HTTP 到 HTTPS 的跳转。验证:
curl -I https://mail.example.com
预期输出:HTTP/2 200,带一个有效的 strict-transport-security 头。如果你遇到重定向循环,检查上面 Nginx 配置里 X-Forwarded-Proto 头是否设好。十有八九,循环就是这个头引起的。
如果这台 VPS 上只跑 Listmonk,那就改用 Caddy。Caddyfile 只有三行,而且无需 cron 任务就能处理证书续期:
mail.example.com {
reverse_proxy 127.0.0.1:9000
}
修复 Message-ID 头
默认情况下,Listmonk 在外发的 Message-ID 头里使用系统主机名。如果你的 VPS 主机名是 localhost 或者任何不是有效 FQDN 的东西,Listmonk 就会发出 Message-ID: <[email protected]>。Gmail 和 Outlook 的垃圾邮件过滤器会立刻把它标记出来。这一点记录在 Cloudron 论坛第 15410 号帖子.
修复方法是 Listmonk 的 config.toml里的一行。对于全新安装,通过 docker compose run --rm app ./listmonk --new-config生成该文件。然后设置:
[app]
hostname = "mail.example.com"
编辑后重启应用容器:
docker compose restart app
在你发出第一封发信活动之前就做好这件事。一个被 localhost.localdomain Message-ID 污染过的列表,比一个一开始就干净的列表更难挽救。
专业提示
如果你想跳过 Compose 的搭建,可以看看我们的 一键 Listmonk VPS 几分钟内一键部署 Listmonk。实例已预装并配好 PostgreSQL。你仍然需要配置你的 SMTP 中继并添加你的 DNS 记录。无论你怎么部署,这些步骤都不是可选的。
选择你的 SMTP 中继

所有发信都经过你配置的中继。中继的 IP 信誉、速率限制和退信处理,才是决定你的邮件落入收件箱还是垃圾邮件箱的因素。
下面是功能对比。定价和免费额度会变。在决定之前,请在各家服务商的官方定价页上逐一核实。
| 提供商 | 成本结构 | 退信 webhook | 最适合 |
|---|---|---|---|
| Amazon SES | 按封计费,量大时极低 | 支持,通过 SNS | 量大时成本低;如果你已在用 AWS |
| Postmark | 月度基础费加按封计费 | 支持,原生 | 送达率优先;事务性邮件信誉好 |
| Brevo | 低发信量有免费额度,更高量有付费档 | 支持 | 低发信量,且有升级路径 |
| Mailgun | 按封计费 | 无原生 webhook 端点;需要时用通用退信 API。 | 开发者熟悉 |
以上只是对各家 SMTP 中继的简略一瞥。接下来,我们会逐一深入讲解。
Amazon SES(推荐的起点)
SES 是量大时最便宜的选择,也是 Listmonk 社区里讨论最多的。它的搭建步骤比 Postmark 或 Brevo 多,但在任何真实发信量下,每封邮件的成本差距都大到足以让这点工作物有所值。
分三个阶段搭建。第一,创建一个带 AmazonSESFullAccess 策略的 IAM 用户(或一个只含 ses:SendRawEmail 以及 ses:GetSendQuota的更收紧的自定义策略)。第二,在 SES 控制台里验证你的发信域名。SES 会带你完成要添加的 DKIM CNAME。第三,在 SES SMTP 设置面板里生成 SMTP 凭据。这些不是你的 AWS 访问密钥;当你点击“Create SMTP credentials”时,SES 会生成一对单独的、专用于 SMTP 的用户名和密码。
在 Listmonk 管理后台的 Settings → SMTP 里,添加一个新服务器:
- 主机:
email-smtp.<region>.amazonaws.com(用你验证域名时所在的 SES 区域) - Port: 587
- Auth protocol: LOGIN
- TLS: STARTTLS
- 用户名和密码:SES 生成的那对 SMTP 凭据
SES 在 587 端口上要求 STARTTLS。 如果你把 TLS 设为 none,或选了 465 端口,Listmonk 会连上,SES 返回 530 Must issue a STARTTLS command first,而管理后台里的 SMTP 凭据测试可能仍然显示成功。在跑任何发信活动之前,先给一个你能掌控的个人收件箱发一封真实的测试邮件。
新的 SES 账号都从沙盒模式开始。在沙盒里你只能发给已验证的邮箱地址,这对订阅者列表毫无用处。从 SES 控制台开一张工单,申请生产访问权限。审批通常需要一个工作日。
Postmark(送达率优先的替代方案)
Postmark 每封邮件的成本比 SES 高,但它原生支持退信 webhook,并且凭借严格的发信人策略,在收件箱送达率上有口碑。如果你的邮件通讯事关业务存亡,或者你不想去管 SES 从沙盒到生产的审批,那它就值这个价。
Listmonk 的配置形式和 SES 一样:主机、587 端口、STARTTLS、来自 Postmark 服务器 API 令牌面板的凭据。在 Postmark 的签名设置里验证你的发信域名,添加 Postmark 生成的 DKIM 记录,你就可以发信了。
当送达率比每封成本更重要时,选 Postmark。当发信量比省心更重要时,选 SES。
关于 SMTP 凭据测试的一个警告。 Listmonk 管理后台里的连接测试总是报告成功,哪怕凭据是错的。这一点记录在 几个 GitHub issue里。别信它。配置任何中继之后,先给单个测试订阅者发一封发信活动,并在目标收件箱里确认收到,再发给你的完整列表。
别用 Mailersend 来做批量发信活动。它每个连接 5 封邮件的上限会产生 421 Service not available 错误,而 Listmonk 会把这些记为已发送,尽管投递其实失败了。这场发信活动在 Listmonk 里看起来很成功,却悄无声息地丢掉了大部分邮件。
让邮件真正送达:SPF、DKIM 和 DMARC

这是你发信域名上的三条 DNS 记录,它们告诉接收方邮件服务器:你的域名授权了这个中继代你发信。漏掉其中任何一条,规模化之后都会有相当一部分发信落进垃圾邮件箱,无论你的中继或文案多干净。在发出第一封发信活动之前,就在你的 DNS 服务商那里把它们加好。
SPF 记录
SPF 授权特定的 IP 或发信服务为你的域名发信。在你发信域名的根上加一条 TXT 记录,带上你中继的 include。对于 SES,这条记录长这样:
v=spf1 include:amazonses.com ~all
对于 Postmark,把 include 换成 include:spf.mtasv.net。务必查阅你中继的官方 SPF 文档,确认确切的 include 值。它因服务商而异,有时还因区域而异。
一个域名只能有一条 SPF 记录。如果你已经为另一项服务(Google Workspace、Microsoft 365)配了一条,就把 include 合并进现有记录,而不是再加一条。
DKIM
DKIM 给外发邮件附上一个加密签名,接收方服务器会用你 DNS 里的公钥来验证它。密钥对由你的中继生成。你把公钥作为一条 TXT 记录加到一个选择器子域名(例如 sel1._domainkey.example.com)上,值就用中继给你的那个确切值。
Listmonk 不负责 DKIM 签名。中继负责。没有 Listmonk 专属的 DKIM 配置。跟着你中继的 DKIM 设置向导走,加上它给你的记录,然后等 DNS 生效(通常不到 30 分钟;有时要几个小时)。
DMARC
DMARC 告诉接收方服务器,对未通过 SPF 或 DKIM 校验的邮件该怎么处理。先用 p=none 进入监控模式,这样你能在汇总报告里看到失败情况,又不会在你排查配置错误期间影响送达率。在以下位置加一条 TXT 记录: _dmarc.example.com:
v=DMARC1; p=none; rua=mailto:[email protected]
两三周的报告都干净之后,再把策略收紧为 p=quarantine or p=reject。别跳过监控阶段。如果你 SPF include 里有个笔误,第一天又配上了 p=reject ,那一上来就会把你自己合法的邮件全部清掉,而且没有任何信号告诉你出了问题。
List-Unsubscribe 头(RFC 8058)由 Listmonk 自动生成。在 Settings → General 下确认它已启用。Gmail 和 Apple Mail 会把这个头呈现为一键退订选项,从而保护发信人信誉。
上线后真正会出问题的地方
四种故障模式,要等你发出第一封真实的发信活动才会冒出来。赶在你的订阅者之前抓住它们。
问题 1:退信率和你中继的数字对不上。 Listmonk 处理退信的方式,是通过 POP3 读取一个指定的退信邮箱地址,并删除它读到的每一封邮件。这其中包括休假回复、投递回执和外出自动回复,全都被归为退信。而你的中继只统计接收方邮件服务器返回的真正投递失败。如果 SES 报告 0.6% 而 Listmonk 报告 4%, 这就是差距所在。修复办法是配置退信 webhook 回调,取代 POP3。对于 SES,用 SNS 把退信通知投递到 Listmonk 的 webhook 端点。对于 Postmark,把它的原生 webhook 指向同一个端点。Webhook 退信是准确的;POP3 退信会虚高。
问题 2:SMTP 凭据测试明明是错的却说成功。 正如中继那一节提到的,连接测试无论凭据是否有效都报告成功。别信它。配置或更改任何 SMTP 设置之后,永远先发一封真实的测试邮件。
问题 3:一场发信活动发到一半就停了,没有报错。 哪怕只有 60% 的订阅者收到了邮件,Listmonk 也会把发信活动标记为 Finished。剩下那些发信,要么被中继拒绝,要么在 VPS 网络层被限流,而 Listmonk 不会把这两种情况作为发信活动级别的错误呈现出来(Cloudron 论坛第 13165 号帖子)。如果一场发信活动显示的发送数少于订阅者数,就打开你中继在该发送时间窗内的仪表盘,把中继接受的数量和 Listmonk 的数量做对比。真相在中继那边。
问题 4:没人在备份 PostgreSQL。 Compose 卷会在重启之间持久化数据。它防不住主机故障、误删 docker 卷,或损坏的升级。加一个每日的 pg_dump:
0 2 * * * docker exec listmonk-postgres pg_dump -U listmonk listmonk > /backups/listmonk-$(date +\%Y\%m\%d).sql
先手动跑一遍这行命令。在你信任这条 cron 条目之前,先确认输出文件非空。一个会写出零字节文件却不报错的备份脚本,比根本没有备份更糟,因为你会从此不再操心它。
在你把这一切交付到生产环境去信任之前,先给一个订阅者发一封测试发信活动,并在目标收件箱里确认收到。如果那一封邮件干净落地,接下来的一万封也会。
常见问题
为什么我的 Listmonk 退信率比 Amazon SES 报告的高?
Listmonk 的 POP3 退信处理会把外出回复和休假自动回复当作退信来读,从而虚高计数。配置 SES 的 SNS webhook 回调以获得准确的计数。
Listmonk 支持事务性邮件吗?
Listmonk 是一个邮件通讯和群发活动工具。它不原生处理事务性邮件(密码重置、订单确认、一对一的触发式邮件)。要从同一个发信域名发事务性邮件,请单独配置你中继的事务性端点,或者在 Listmonk 之外使用 Postal 或 Postmark 的事务性 API 这类专门工具。
我怎么把我的 Mailchimp 订阅者导入 Listmonk?
从 Audience → Export Audience 把你的 Mailchimp 列表导出为 CSV。在 Listmonk 里,进入 Subscribers → Import 并上传该 CSV。出现提示时映射 email 和 name 列。Listmonk 接受来自 Mailchimp、ConvertKit 以及大多数邮件通讯平台的标准 CSV 导出,无需格式转换。
当有人退订一场 Listmonk 发信活动时会发生什么?
Listmonk 默认在每一封发信活动邮件里加一个退订链接。当订阅者点击它时,他们会被加入屏蔽列表,并从所有未来的发信活动中移除。List-Unsubscribe 头(RFC 8058)会自动包含,因此支持一键退订的邮件客户端(Gmail、Apple Mail)会原生呈现它。该订阅者的记录会出于审计目的留在数据库里,但不会再有发信活动发给他们。