listmonk 配置完全指南:TOML 配置、环境变量、HTTP 路由与生产环境调优
【免费下载链接】listmonkHigh performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.项目地址: https://gitcode.com/GitHub_Trending/li/listmonk
listmonk 是一款自托管的开源邮件列表与新闻通讯管理器,整个应用打包为单一二进制文件。本指南以仓库官方文档 docs/docs/content/configuration.md 为主线,系统讲解 listmonk 的完整配置体系:包括 TOML 配置文件与LISTMONK_环境变量的读写方式、管理后台 Settings 与配置文件的分工边界、需要暴露给公网与仅限私有的 HTTP 路由清单、媒体上传的三种挂载方案、日志采集、时区、SMTP 重试策略以及大列表场景下的批处理性能调优。读完本指南,你将能够独立完成 listmonk 从安装到生产环境部署的整套配置工作。
TOML 配置文件:加载顺序与生成方式
listmonk 使用 TOML 作为原生配置文件格式。通过--config命令行参数可以指定一个或多个配置文件,且该参数可以重复出现多次,例如:
./listmonk --config config.toml --config extra.toml多个文件会按传入顺序依次合并加载,后者覆盖前者的同名键值。这一点在源码中有直接体现:命令行 flag 的定义位于 cmd/init.go,其中config被注册为一个StringSlice类型的 flag,注释明确写着"path to one or more config files (will be merged in order)";对应的加载逻辑在 initConfigFiles 中,逐个文件通过 koanf 的file.Provider与toml.Parser读入内存。
如果启动时指定的配置文件不存在,listmonk 会直接报错退出,并提示"config file not found. If there isn't one yet, run --new-config to generate one."。要生成一份全新的示例配置,只需执行:
./listmonk --new-config该命令会在--config指定的第一个路径处生成样例文件(见 cmd/main.go 与 cmd/install.go 的newConfigFile实现)。仓库根目录下的 config.toml.sample 就是这份样例的完整内容,它包含两个核心小节:
[app] # 应用 Web 服务器的监听地址与端口。默认 localhost 只接受本机连接; # 监听所有网卡用 '0.0.0.0';监听 80 端口需要提权运行。 address = "localhost:9000" # 数据库。 [db] host = "localhost" port = 5432 user = "listmonk" password = "listmonk" # 确保该数据库已在 Postgres 中创建。 database = "listmonk" ssl_mode = "disable" max_open = 25 max_idle = 25 max_lifetime = "300s" # 可选:空格分隔的 Postgres DSN 附加参数,例如 "application_name=listmonk gssencmode=disable" params = ""其中[db]各字段在 initDB 中被逐一解析并拼装为 Postgres DSN:host、port、user、password、database(映射为dbname)、ssl_mode(映射为sslmode)、params为附加 DSN 参数,max_open/max_idle/max_lifetime则分别对应数据库连接池的最大打开连接数、最大空闲连接数与连接最大存活时长。
需要特别强调的是配置的分层设计:除少量底层配置变量和数据库配置外,其余几乎所有设置(站点名称、SMTP、媒体、隐私、性能等)都可以直接在管理后台的Settings面板中修改并持久化到数据库。这一点在 initSettings 中体现——应用启动时会把数据库中保存的设置(键为点分路径,如app.favicon_url)反序列化并合并进 koanf 配置树,因此后台修改会实时生效。
环境变量配置:LISTMONK_ 前缀与 __ 双下划线
config.toml中定义的任何变量都可以用环境变量的形式提供,规则如下:
- 环境变量统一以
LISTMONK_为前缀; - 配置层级之间的
.(点号)替换为__(双下划线)。
官方文档给出的受支持变量清单如下:
| 环境变量 | 示例值 |
|---|---|
LISTMONK_app__address | "0.0.0.0:9000" |
LISTMONK_db__host | db |
LISTMONK_db__port | 9432 |
LISTMONK_db__user | listmonk |
LISTMONK_db__password | listmonk |
LISTMONK_db__database | listmonk |
LISTMONK_db__ssl_mode | disable |
例如LISTMONK_app__address=0.0.0.0:9000就等价于在 TOML 中写[app] address = "0.0.0.0:9000"。
如果希望纯环境变量启动、完全不使用配置文件,只需把--config参数置为空字符串:
LISTMONK_app__address=0.0.0.0:9000 \ LISTMONK_db__host=db \ LISTMONK_db__port=5432 \ LISTMONK_db__user=listmonk \ LISTMONK_db__password=listmonk \ LISTMONK_db__database=listmonk \ LISTMONK_db__ssl_mode=disable \ ./listmonk --config=""这正是仓库根目录 docker-compose.yml 所采用的方式:容器内启动命令为./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config '',全部配置通过environment中的LISTMONK_*变量注入,同时支持LISTMONK_ADMIN_USER/LISTMONK_ADMIN_PASSWORD在首次启动时自动创建超级管理员账号。此外,该 compose 文件还演示了LISTMONK_*_FILE模式——所有LISTMONK_*环境变量都支持对应的LISTMONK_*_FILE变体,用于通过 Docker Secrets / Podman 从文件中读取密钥,避免敏感信息出现在环境变量或 compose 文件中。
需要留意的一点是:环境变量方式只覆盖 TOML 中所能表达的低层配置;那些由后台 Settings 管理的设置(如 SMTP 服务器、消息速率等)仍存于数据库,与配置文件/环境变量是两套独立的存储。
自定义系统模板
listmonk 内置的系统模板用于渲染对外公开页面(如订阅管理页)以及自动生成的系统邮件(如确认订阅的 opt-in 邮件)。官方文档将这部分细节指向 docs/docs/content/templating.md:要自定义这些模板,可把仓库中的static目录复制到本地,然后通过启动参数指向它:
./listmonk --static-dir=your/custom/path与之配套的还有--i18n-dir参数,用于覆盖内置的 i18n 语言文件目录(两个 flag 均定义于 cmd/init.go)。从源码 initFS 可以看到,嵌入式文件系统会优先加载用户指定的 static/i18n 目录中的文件并覆盖默认资源;仓库中 static/email-templates 目录存放了base.html、campaign-status.html、subscriber-optin.html等系统邮件模板,可供参考与定制。
HTTP 路由清单:私有端点与公网端点
在配置反向代理(auth proxy)或 Web 应用防火墙(WAF)时,需要精确区分 listmonk 的哪些路由必须保持私有、哪些必须暴露到公网。官方文档给出了权威路由表,与 cmd/handlers.go 中的实际注册代码完全一致。
私有管理端点(不得直接暴露公网)
| 方法 | 路由 | 说明 |
|---|---|---|
* | /api/* | 管理端 API |
GET | /admin/* | 管理端 UI 与 HTML 页面 |
POST | /webhooks/bounce | 管理端弹回(bounce)Webhook,需鉴权 |
其中/webhooks/bounce在源码中受权限webhooks:post_bounce保护,属于需要登录的私有接口。
需暴露到公网的端点
| 方法 | 路由 | 说明 |
|---|---|---|
GET, POST | /subscription/* | 订阅相关 HTML 页面(订阅表单、退订页、opt-in 页、数据导出/清除等) |
GET | /link/* | 被追踪链接的重定向(/link/:linkUUID/:campUUID/:subUUID) |
GET | /campaign/* | 像素追踪图片(/campaign/:campUUID/:subUUID/px.png)及网页版邮件视图 |
GET | /public/* | 订阅页面的静态资源文件 |
POST | /webhooks/service/* | SES、Azure ACS、Sendgrid 等提供商托管的弹回 Webhook 端点 |
GET | /uploads/* | 媒体设置中配置的文件上传路径 |
源码层面的对应关系为:/subscription/form、/subscription/:campUUID/:subUUID、/subscription/optin/:subUUID、/subscription/export/:subUUID、/subscription/wipe/:subUUID、/link/:linkUUID/:campUUID/:subUUID、/campaign/:campUUID/:subUUID、/campaign/:campUUID/:subUUID/px.png均注册在公开路由组中(见 cmd/handlers.go);/webhooks/service/:service同样公开注册(cmd/handlers.go)。这些 URL 模式的拼接逻辑见 initUrlConfig,例如追踪链接格式为{root}/link/{campaign_uuid}/{subscriber_uuid}/{link_uuid},像素追踪为{root}/campaign/{campaign_uuid}/{subscriber_uuid}/px.png。
需要提醒的是,/link/*与/campaign/*会解析 UUID 并可能触发数据库查询,若您的反向代理对公网路由有频率限制策略,应结合订阅流量的实际体量评估。
媒体上传:filesystem 提供商与容器挂载
listmonk 的媒体上传支持 filesystem 与 S3 两种提供商(由upload.provider配置选择,见 initMediaStore)。使用 filesystem 提供商时,上传文件会写入容器内配置的目录,因此在 Docker 部署中需要把该目录挂载到宿主机。官方文档提供了两种挂载方式。
注意:无论采用哪种方式,修改 docker-compose 后都需要执行
sudo docker compose stop ; sudo docker compose up使其生效;并在https://listmonk.mysite.com/admin/settings的媒体设置中,将上传目录填写为容器内路径(如/listmonk/uploads)。
方式一:Docker 命名卷(volumes)
使用 Docker 命名卷时,指定卷名称与容器内目标路径即可:
app: volumes: - type: volume source: listmonk-uploads target: /listmonk/uploads volumes: listmonk-uploads:该卷由 Docker 自身管理,可通过docker volume inspect listmonk_listmonk-uploads查看其宿主机上的真实存储路径。
方式二:绑定挂载(bind mounts)
绑定挂载直接把宿主机目录映射进容器:
app: volumes: - ./path/on/your/host/:/path/inside/container例如把宿主机./data/uploads映射为容器内/listmonk/uploads:
app: volumes: - ./data/uploads:/listmonk/uploads上传后的文件会落在宿主机/data/uploads目录中。如果使用默认目录名:
app: volumes: - ./uploads:/listmonk/uploads仓库自带 docker-compose.yml 采用的正是这一默认绑定挂载方式(./uploads:/listmonk/uploads:rw),并注明使用时需在 Admin → Settings → Media 中将目录改为/listmonk/uploads。
从实现上看,filesystem 提供商由 internal/media/providers/filesystem/filesystem.go 实现:其Opts结构包含upload_path(磁盘写入路径)、upload_uri(对外访问 URI)与root_url三个字段;文件写入磁盘后,通过{root_url}{upload_uri}/{filename}拼接出对外可访问的 URL(见 filesystem.go)。对应的静态文件服务注册在 initHTTPServer:当upload.provider == "filesystem"且upload_uri非空时,会以该 URI 为前缀提供静态文件服务。
日志:Docker 与二进制两种形态
Docker 部署
Docker 模式下,listmonk 的日志输出到容器的/dev/stdout与/dev/stderr,由 Docker daemon 收集,默认存放在宿主机的节点路径中。常用命令如下:
sudo docker logs -f sudo docker logs listmonk_app -t sudo docker logs listmonk_db -t sudo docker logs --help查看容器元数据(含挂载卷、网络、启动配置等):
sudo docker inspect listmonk_listmonk日志的采集驱动、轮转(logrotate)策略等可在 Docker daemon 配置文件/etc/docker/daemon.json中统一配置,例如切换 logging driver 或设置日志轮转上限。
二进制部署
直接运行二进制时,listmonk 把日志写到stdout,默认不会落盘。要保存为文件,使用 shell 重定向:
./listmonk > listmonk.log管理后台的Settings → Logs页面会展示标准日志输出的最近 1000 行,但该缓冲会在 listmonk 重启后清空——它只是内存中的滚动日志视图,并非持久化日志文件。
若使用仓库自带的 systemd 服务文件 listmonk@.service,可参照官方建议修改ExecStart,把日志同时追加写入文件,使其在重启后仍然保留:
ExecStart=/bin/bash -ce "exec /usr/bin/listmonk --config /etc/listmonk/config.toml --static-dir /etc/listmonk/static >>/etc/listmonk/listmonk.log 2>&1"时区设置
listmonk 的日志时间戳等时区行为跟随进程的TZ环境变量。在 Docker 部署中,编辑docker-compose.yml的 app 服务环境变量即可:
environment: - TZ=Etc/UTCTZ可取 tz database 中列出的任意合法时区(如Asia/Shanghai、Europe/Berlin等)。修改后执行sudo docker-compose stop ; sudo docker-compose up使其生效。仓库默认 docker-compose.yml 中即包含TZ: Etc/UTC。
SMTP:重试次数与出站端口
重试次数(Retries)
后台Settings → SMTP → Retries表示:一条消息在发送瞬间失败时,listmonk 会从 SMTP 连接池中换用不同的连接静默重试的次数。重试仍失败的消息才会被记录为错误并忽略。也就是说,该参数直接决定了瞬时性失败(如连接抖动)被容忍的程度,调大可提升投递鲁棒性,但会放大 SMTP 服务器的瞬时故障对发送延迟的影响。
从架构上看,SMTP 连接池与多服务器支持在 initSMTPMessengers 中初始化:多个 SMTP 服务器可以按enabled开关加载,并合并为一个名为email的 messenger;若服务器配置了name,还会额外注册为独立的email / $namemessenger,供单个 campaign 单独选用。
出站端口被封
部分云厂商/主机商会屏蔽出站 SMTP 端口(常见为 25、465)。在联系主机商解除封锁之前,listmonk 将无法正常发送邮件。建议部署前先验证本机到目标 SMTP 服务器的出站连通性,再在后台配置 SMTP 服务器。
性能调优:批处理大小(Batch Size)
batch_size是面向百万级订阅者大列表的关键性能参数,用于最大化发送吞吐。它的语义是:campaign 运行期间,listmonk 每轮处理周期(约 5 秒)从数据库顺序拉取的订阅者数量。
- 调大
batch_size:单次拉取的订阅者更多,内存占用上升,但减少了与数据库之间的往返次数(round trip),从而提升吞吐; - 调小
batch_size:内存占用更低,但数据库查询更频繁。
该参数在 initCampaignManager 中被读取并注入 campaign manager 的Config.BatchSize,实际拉取逻辑见 internal/manager/pipe.go 的NextSubscribers——每次以BatchSize为上限调用store.NextSubscribers(camp.ID, p.m.cfg.BatchSize)。同时在 internal/manager/manager.go 中可以看到防御性默认值:若batch_size < 1则回退为1000,concurrency < 1回退为 1,message_rate < 1回退为 1。数据库侧的出厂默认值则记录在迁移脚本 internal/migrations/v0.7.0.go 中:app.batch_size = 1000、app.concurrency = 10、app.message_rate = 10、app.max_send_errors = 1000。
围绕吞吐量还有几个与之配套、同样在 Settings 中可调的参数值得一并理解:
- Concurrency:并发处理消息的 worker 数量(
app.concurrency); - Message rate:每名 worker 每秒发送的消息数上限(
app.message_rate); - Max send errors:发送错误累计达到该阈值时,campaign 会被自动停止(
app.max_send_errors)。
对于千万级订阅者场景,建议根据可用内存实测调高batch_size,并结合 concurrency / message rate 共同权衡,避免内存溢出或 SMTP 服务器过载。此外,若需运行多个 listmonk 实例(如一个仅对外服务、一个专职处理 campaign),可配合--passive参数让实例跳过 campaign 扫描与发送(见 cmd/init.go 与 initCampaignManager)。
小结
listmonk 的配置体系由两条清晰的路径构成:启动期静态配置(TOML 文件 +LISTMONK_环境变量,负责监听地址、数据库连接等底层参数)与运行期动态配置(后台 Settings,负责 SMTP、媒体、隐私、性能等业务参数)。配合本指南梳理的 HTTP 路由清单、媒体挂载方案、日志与时区策略,即可在 Docker 或裸二进制环境下完成一套规范、可运维、面向大规模列表的生产级配置。
【免费下载链接】listmonkHigh performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.项目地址: https://gitcode.com/GitHub_Trending/li/listmonk
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考