Listmonk 6.2.0 进阶实战与运维
Listmonk 6.2.0 进阶运维实操:SMTP 配置与连通性测试、Bounce 退信 webhook 上报与自动拉黑、POP3 扫描排障、v6.1.0 升级 v6.2.0 迁移实录、pg_dump 备份与恢复演练、API 用户与权限管理
Listmonk 6.2.0 进阶实战与运维
目标:面向已按《Docker Compose 部署指南》完成 Listmonk 6.2.0 部署的运维场景,实操四类高频任务:SMTP 发信通道配置与连通性测试、Bounce(退信)上报与自动拉黑验证、版本升级(v6.1.0 → v6.2.0 数据库迁移实录)、pg_dump 备份与恢复演练,并覆盖 API 用户与权限管理。本文所有命令与回显均在 v6.2.0 环境实测通过。
一、环境信息
- Listmonk 版本:v6.2.0(升级实验自 v6.1.0 原地升级,compose 镜像 tag
listmonk/listmonk:v6.2.0) - 数据库:PostgreSQL 17(compose 服务
db,数据卷持久化) - 测试收信:Mailpit(SMTP 1025 端口收信、8025 端口 Web 收件箱,与 app 同网络)
- API 认证:
curl -u "opsapi:<TOKEN>"(API 用户opsapi,Super Admin 角色)
二、SMTP 发信配置与连通性测试
v6 起 SMTP 配置存放于数据库运行时设置(环境变量方式无效,原理见《配置参数详解》第七章)。以 Mailpit 为发信目标(测试环境不真正外发):
GET /api/settings拉取完整设置对象,修改smtp[0]:enabled=true、host=mailpit、port=1025、auth_protocol=""、tls_type="none"、max_conns=2;PUT /api/settings全量回传,实测返回{"data":true};- 回读
GET /api/settings确认smtp[0].host=mailpit写入成功。
连通性测试调用 POST /api/settings/smtp/test——请求体是完整 SMTP 配置 JSON 外加一个 email 收件地址字段(不是传 smtp_uuid):
curl -s -o /dev/null -w "%{http_code}\n" \
-u "opsapi:<TOKEN>" \
-H "Content-Type: application/json" \
-d '{"host":"mailpit","port":1025,"auth_protocol":"","enabled":true,"tls_type":"none","username":"","password":"","email":"admin@listmonk.local"}' \
http://localhost:9000/api/settings/smtp/test
实测回显:
200
打开 Mailpit 收件箱(8025 端口),实测收到收件人 admin@listmonk.local、主题 Test connection 的测试邮件,发信链路验收通过。此后 Campaign 投递即走该通道。
三、退信(Bounce)处理实战
3.1 Bounce 相关 API 路由
v6.2.0 实测可用的退信相关路由:
| 路由 | 方法 | 说明 |
|---|---|---|
/api/bounces | GET | 查询退信记录 |
/api/bounces/:id | GET / DELETE | 查看单条 / 删除 |
/api/bounces/blocklist | PUT | 按退信记录批量拉黑 |
/webhooks/bounce | POST | 自建退信上报 webhook(需 webhooks:post_bounce 权限) |
/webhooks/service/:service | POST | 服务商公共退信 webhook,service 取值实测支持 ses、sendgrid、postmark、azure、forwardemail、lettermint |
⚠️ 版本差异:旧版本的
POST /api/bounces(直接 API 上报退信)在 v6 已移除——实测调用返回 404unknown endpoint。上报退信请走/webhooks/bounce(带认证)或/webhooks/service/:service(服务商回调)。
3.2 模拟硬退信并验证自动拉黑
用 webhook 上报一封 hard(硬退信,地址不存在类)退信,关联此前发送的 Campaign(id 2):
curl -s -u "opsapi:<TOKEN>" \
-H "Content-Type: application/json" \
-d '{"email":"zhao.lei@example.com","campaign_id":2,"type":"hard","meta":{"diagnostic":"550 5.1.1 user unknown"}}' \
http://localhost:9000/webhooks/bounce
实测回显:
{"data":true}
查询退信记录:
curl -s -u "opsapi:<TOKEN>" http://localhost:9000/api/bounces
实测回显(节选):
{"data":{"results":[...],"total":1}}
total: 1,退信已入库。按《配置参数详解》7.1 节的默认策略(hard 1 次即 blocklist),核对该订阅者状态——实测订阅者 6(zhao.lei@example.com)的 status 已自动变为 blocklisted,硬退信自动拉黑链路验证通过。
💡 技术拓展:
type三态对应真实世界的退信分类——hard是永久性失败(550 user unknown、域名不存在),重试无意义;soft是暂时性失败(邮箱满 452、服务暂忙 4xx),保留重试价值;complaint是收件人标记垃圾邮件的 FBL 投诉回传。生产环境将/webhooks/service/:service对接到 SES/SendGrid 等服务商的退信回调,即可全自动处置失效地址、保护发信信誉。
3.3 POP3 邮箱扫描配置与排障
除 webhook 外,Listmonk 还可定时从 POP3 邮箱扫描退信通知。配置位于 Settings:bounce.enabled=true、bounce.webhooks_enabled=true,并在 bounce.mailboxes 中添加邮箱(结构示例):
[{
"enabled": true, "type": "pop",
"host": "mail.example.com", "port": 1110,
"auth_protocol": "userpass",
"username": "bounce@listmonk.local", "password": "password",
"tls_type": "none", "scan_interval": "1m", "return_path": ""
}]
两个实测排障结论:
- 扫描间隔下限 1 分钟:
scan_interval设30s保存被拒,报错Bounce scan interval should be minimum 1 minute.,改为1m及以上; - Mailpit 的 POP3 默认关闭:测试环境用 Mailpit 模拟退信邮箱时,POP3 服务需以
--pop3(默认 1110 端口)与--pop3-auth-file认证文件参数显式启用,否则 Listmonk 扫描日志持续出现error dial ... connection refused——该现象即 POP3 端口未监听所致,不是 Listmonk 配置错误。生产环境直接对接真实邮箱服务商或自建 Dovecot 的 POP3 即可。
四、版本升级实操(v6.1.0 升级 v6.2.0)
官方 compose 启动命令自带 --upgrade --yes,升级即「换镜像 tag → 重建 app → 自动迁移」三步:
sed -i 's|listmonk/listmonk:v6.1.0|listmonk/listmonk:v6.2.0|' docker-compose.yml
docker compose up -d app
实测升级日志(docker compose logs app 节选):
2026/10/17 00:35:06.902686 main.go:106: v6.2.0 (ef0a7587 2026-06-26T10:21:29Z, linux/amd64)
2026/10/17 00:35:06.953000 main.go:165: running upgrade...
2026/10/17 00:35:06.957679 upgrade.go:80: running migration v6.2.0
2026/10/17 00:35:06.995526 upgrade.go:98: upgrade complete
从 running migration v6.2.0 到 upgrade complete 全程不足 1 秒。升级后实测确认:
- SMTP 发信通道重新初始化为
@mailpit(数据库中的 Settings 随迁移延续,无需重配); - Bounce 扫描任务随服务启动恢复;
- 既有订阅者、列表、Campaign 数据完整无损。
回滚须知:数据库迁移不会自动回滚。回滚 = compose 镜像 tag 换回旧版本 + 恢复升级前的数据库备份(见第五章);只换镜像不恢复备份可能造成新表结构与旧代码不兼容。
五、数据库备份与恢复
5.1 备份
用 pg_dump 导出整个业务库:
docker exec listmonk_db pg_dump -U listmonk -d listmonk > backup-20261017.sql
ls -lh backup-20261017.sql
head -n 1 backup-20261017.sql
grep -c "CREATE TABLE" backup-20261017.sql
实测结果:
-rw-r--r-- 1 root root 74339 Oct 17 00:38 backup-20261017.sql
-- PostgreSQL database dump
16
备份文件 74339 字节、纯 SQL 文本格式、包含 16 张业务表的完整结构与数据(文件头部为 -- PostgreSQL database dump)。生产建议每日定时执行并异地留存。
5.2 恢复演练
恢复目标库必须先清空。⚠️ 实测排障:app 在运行时直接对 listmonk 库执行 DROP 会报错:
ERROR: cannot drop the currently open database
原因是 Listmonk 进程正连接占用该库。标准流程是先停 app,再经 postgres 系统库执行删除重建:
docker compose stop app
docker exec -i listmonk_db psql -U listmonk -d postgres \
-c "DROP DATABASE listmonk;" -c "CREATE DATABASE listmonk OWNER listmonk;"
实测回显:
DROP DATABASE
CREATE DATABASE
回灌备份:
docker exec -i listmonk_db psql -U listmonk -d listmonk < backup-20261017.sql
实测回灌正常完成,输出末尾为备份文件最后的 REFRESH MATERIALIZED VIEW 语句(备份文件以物化视图刷新收尾)。
docker compose start app
5.3 恢复后验证
服务起来后核验数据完好性(凭据同前):
curl -s -u "opsapi:<TOKEN>" "http://localhost:9000/api/subscribers?list_id=3"
实测回显(节选):
{"data":{"total":5,...}}
curl -s -u "opsapi:<TOKEN>" http://localhost:9000/api/campaigns/2
实测回显(节选):
{"data":{"id":2,"status":"finished","to_send":5,"sent":5,...}}
订阅者仍为 5 人、Campaign 2 投递记录完整(finished / sent 5),恢复演练闭环通过。
六、API 用户与权限管理
6.1 创建 API 用户
API 用户的创建走 POST /api/users(需管理员登录会话,或在后台 Admin → Users 页面操作),请求体:
{
"username": "opsapi",
"name": "Ops API",
"type": "api",
"status": "enabled",
"user_role_id": 1
}
实测响应(节选):
{"data":{"id":2,"username":"opsapi","password":"BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ","type":"api","status":"enabled","user_role":{"id":1,"name":"Super Admin","permissions":[...28 项...]}}}
三个易错点(实测复现过的报错):
- 缺
status字段 →pq: invalid input value for enum user_status; - 角色字段误写为
roles→null value in column user_role_id violates not-null constraint(正确字段名是user_role_id); - 响应中的
password即 48 位 API Token,仅在创建时完整返回一次,遗失只能重建用户。
6.2 权限模型
Super Admin 角色共 28 项细粒度权限,按资源前缀归类(实测清单节选):
| 资源域 | 权限(节选) |
|---|---|
| 订阅者 | subscribers:get、subscribers:get_all、subscribers:manage、subscribers:import、subscribers:sql_query |
| 列表 | lists:get_all、lists:manage_all |
| Campaign | campaigns:get、campaigns:get_all、campaigns:manage、campaigns:manage_all、campaigns:send、campaigns:get_analytics |
| 退信 | bounces:get、bounces:manage、webhooks:post_bounce |
| 设置 | settings:get、settings:manage、settings:maintain |
| 其他 | templates:get/manage、media:get/manage、users:get/manage、roles:get/manage、tx:send |
💡 技术拓展:
subscribers:sql_query允许通过 SQL 查询接口直接筛选订阅者,是高级集成的强力但高危权限;tx:send用于事务邮件(如注册验证码、订单通知)单独发送 API。生产集成应按最小权限原则创建专用 API 用户——例如只做退信上报的服务商回调账号仅需webhooks:post_bounce。
6.3 API 调用方式
curl -s -o /dev/null -w "%{http_code}\n" -u "opsapi:<TOKEN>" http://localhost:9000/api/lists
实测回显:
200
认证为 HTTP Basic:用户名 = API 用户名,密码 = API Token(不是网页登录密码;误用登录密码返回 403 invalid API credentials)。
七、运维命令速查
本文实测用到的运维操作一览(compose 部署形态):
| 场景 | 命令 | 实测结果 |
|---|---|---|
| SMTP 连通性测试 | POST /api/settings/smtp/test | 200 + 测试邮件实收 |
| 退信上报 | POST /webhooks/bounce | {"data":true},hard 1 次自动 blocklist |
| 退信记录查询 | GET /api/bounces | total: 1 |
| 版本升级 | 改 tag → docker compose up -d app | running migration v6.2.0 → upgrade complete |
| 备份 | docker exec listmonk_db pg_dump -U listmonk -d listmonk > 备份.sql | 74339 字节 / 16 张表 |
| 恢复 | 停 app → -d postgres 删重建库 → psql < 备份.sql → 起 app | 数据完好 |
| 服务状态 | docker compose ps | app Up / db Up (healthy) |
日常巡检建议:定期跑一次 SMTP 测试确认发信通道;关注 GET /api/bounces 的退信增量与 blocklist 增长;升级前先 pg_dump 留好还原点。
八、参考链接
- Listmonk API 文档:https://listmonk.app/docs/apis/
- Bounce 处理文档:https://listmonk.app/docs/bounces/
- 升级说明:https://listmonk.app/docs/upgrade/
- PostgreSQL pg_dump 文档:https://www.postgresql.org/docs/17/app-pgdump.html
- GitHub 仓库:https://github.com/knadh/listmonk