Listmonk 6.2.0 进阶实战与运维

Listmonk 6.2.0 进阶运维实操:SMTP 配置与连通性测试、Bounce 退信 webhook 上报与自动拉黑、POP3 扫描排障、v6.1.0 升级 v6.2.0 迁移实录、pg_dump 备份与恢复演练、API 用户与权限管理

ListmonkListmonkLinuxDocker ComposePostgreSQL2026/10/17

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 为发信目标(测试环境不真正外发):

  1. GET /api/settings 拉取完整设置对象,修改 smtp[0]: enabled=true、host=mailpit、port=1025、auth_protocol=""、tls_type="none"、max_conns=2;
  2. PUT /api/settings 全量回传,实测返回 {"data":true};
  3. 回读 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/bouncesGET查询退信记录
/api/bounces/:idGET / DELETE查看单条 / 删除
/api/bounces/blocklistPUT按退信记录批量拉黑
/webhooks/bouncePOST自建退信上报 webhook(需 webhooks:post_bounce 权限)
/webhooks/service/:servicePOST服务商公共退信 webhook,service 取值实测支持 ses、sendgrid、postmark、azure、forwardemail、lettermint

⚠️ 版本差异:旧版本的 POST /api/bounces(直接 API 上报退信)在 v6 已移除——实测调用返回 404 unknown 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
Campaigncampaigns: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/test200 + 测试邮件实收
退信上报POST /webhooks/bounce{"data":true},hard 1 次自动 blocklist
退信记录查询GET /api/bouncestotal: 1
版本升级改 tag → docker compose up -d apprunning migration v6.2.0 → upgrade complete
备份docker exec listmonk_db pg_dump -U listmonk -d listmonk > 备份.sql74339 字节 / 16 张表
恢复停 app → -d postgres 删重建库 → psql < 备份.sql → 起 app数据完好
服务状态docker compose psapp Up / db Up (healthy)

日常巡检建议:定期跑一次 SMTP 测试确认发信通道;关注 GET /api/bounces 的退信增量与 blocklist 增长;升级前先 pg_dump 留好还原点。

八、参考链接