Mailman 3.3.10 配置参数详解
Mailman 3.3.10 三份配置文件逐段详解:mailman.conf 生效配置、REST 凭据/归档开关/LMTP 端口/白名单/max_message_size 五组改生效验证闭环实测与生产模板。
Mailman 3.3.10 配置参数详解
目标:逐段讲透 GNU Mailman 3.3.10 的配置体系——Core 的
/etc/mailman3/mailman.cfg、归档插件的mailman-hyperkitty.cfg、Web 面的/etc/mailman3/settings.py,每个关键参数都给出「默认值实测 → 修改 → 生效 → 行为验证」的闭环。已在 Ubuntu 22.04.5 + Mailman 3.3.10 实测通过,安装过程见 《Ubuntu 22.04 安装 Mailman 指南》。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS,主机名
list.example.org; - Mailman 3.3.10(Tom Sawyer)+ mailman-web 0.0.9(Postorius 1.3.13 / HyperKitty 1.3.12)+ mailman-hyperkitty 归档插件;
- 配置文件:
/etc/mailman3/mailman.cfg(Core)、/etc/mailman3/mailman-hyperkitty.cfg(归档插件)、/etc/mailman3/settings.py(Web 面); - 运行数据:
/opt/mailman/var([paths.custom] var_dir);Web 数据/opt/mailman/web; - 服务:Core(systemd
mailman3,REST 8001 / LMTP 8024)、Web 面(systemdmailman-web,uWSGI 8000,nginx 80)、postfix 25; - REST 凭据:restadmin / q1w2e3r4;演示列表:test@list.example.org,成员 alice@example.org、bob@mbox.example.org。
二、前置条件
- 已按 《Ubuntu 22.04 安装 Mailman 指南》 完成安装,
systemctl is-active mailman3 mailman-web均为 active; - 能以
sudo -u mailman执行 Core 命令行; - 能以 restadmin/q1w2e3r4 访问
http://127.0.0.1:8001/3.1/REST API(curl)。
三、参数查看与修改方式
3.1 查看:mailman conf 是生效配置的唯一权威来源
mailman conf 打印的是合并后的最终生效配置(含默认值),而不是文件原文。帮助如下(实测):
sudo -u mailman /opt/mailman/venv/bin/mailman conf --help
Usage: mailman conf [OPTIONS]
Print the Mailman configuration.
Options:
-o, --output FILENAME File to send the output to. If not given, or if '-'
is given, standard output is used.
-s, --section TEXT Section to use for the lookup. If no key is given,
all the key-value pairs of the given section will be
displayed.
-k, --key TEXT Key to use for the lookup. If no section is given,
all the key-values pair from any section matching the
given key will be displayed.
-h, --help Show this message and exit.
实用组合(实测):
sudo -u mailman /opt/mailman/venv/bin/mailman conf -s mta # 打印 [mta] 全段
sudo -u mailman /opt/mailman/venv/bin/mailman conf -k site_owner # 跨段找同名键
sudo -u mailman /opt/mailman/venv/bin/mailman conf | grep -E '^\[(mailman)\]' # 过滤某段
-s mta 的实测输出(节选):
[mta] configuration: /opt/mailman/etc/postfix-mailman.cfg
[mta] incoming: mailman.mta.postfix.LMTP
[mta] lmtp_host: 127.0.0.1
[mta] lmtp_port: 8024
[mta] outgoing: mailman.mta.deliver.deliver
[mta] smtp_host: 127.0.0.1
[mta] smtp_port: 25
[mta] max_recipients: 10
⚠️ 注意:
mailman conf是只读工具,没有「写回」参数。修改一律编辑配置文件 + 重启服务,再用mailman conf验证生效。
3.2 三份配置文件的分工与查找顺序
| 文件 | 归属 | 生效方式 |
|---|---|---|
/etc/mailman3/mailman.cfg | Core(含 [archiver.hyperkitty] 段) | systemctl restart mailman3 |
/etc/mailman3/mailman-hyperkitty.cfg | 归档插件(由 mailman.cfg 的 configuration 键指定) | systemctl restart mailman3 |
/etc/mailman3/settings.py | Django Web 面(Postorius + HyperKitty) | systemctl restart mailman-web |
Core 的配置查找顺序(源码 mailman/core/initialize.py 实读):
$MAILMAN_CONFIG_FILE → ./mailman.cfg → ./var/etc/mailman.cfg → ~/.mailman.cfg
→ /etc/mailman.cfg → /etc/mailman3/mailman.cfg
两个实测行为(详见安装篇 4.3 节):root 运行需 --run-as-root;所有路径都找不到配置时会在当前目录自动生成默认配置并遮蔽后续查找。规范:配置只放 /etc/mailman3/mailman.cfg 一处。
💡 技术拓展:
settings.py并没有「默认文件」,mailman-web的约定是MAILMAN_WEB_CONFIG环境变量指向一个 Python 模块(默认/etc/mailman3/settings.py),文件开头from mailman_web.settings.base import *与from mailman_web.settings.mailman import *继承官方默认值后逐项覆盖。这套「defaults + 覆盖层」的思路与 mailman.cfg 的「内置默认 + 用户文件 push」一致:你只写差异项,其余继承官方默认。
3.3 修改与生效的标准流程
sudo vi /etc/mailman3/mailman.cfg # 1. 修改
sudo systemctl restart mailman3 # 2. 生效(Core)
sudo -u mailman /opt/mailman/venv/bin/mailman conf -s <段> # 3. 验证值已变
# 4. 行为级验证(发信/REST/日志),见第五章各闭环实验
Web 面同理:改 settings.py → systemctl restart mailman-web → curl 行为验证。
四、参数总览
以下为本文环境 mailman conf 实测默认值(本文 mailman.cfg 已按安装篇配置,标注「默认」的值为官方内置默认):
[mailman] 站点与消息处理
| 键 | 实测值 | 说明 |
|---|---|---|
| site_owner | admin@example.org | 站点所有者,OWNER 通知收件人 |
| layout | custom | 路径布局,对应 [paths.custom] |
| default_language | en | 新列表默认语言 |
| sender_headers | from from_ reply-to sender | 依序用于判定发件人的头 |
| noreply_address | noreply | 自动回复的免回地址名 |
| cache_life | 7d | 缓存寿命 |
| pending_request_life | 3d | 待审请求(held)默认保存期 |
| moderator_request_life | 180d | 审批请求保存期 |
| listname_chars | [-_.0-9a-z] | 合法列表名字符集 |
| check_max_size_on_filtered_message | no | 内容过滤后再查体积(默认过滤前查) |
| run_tasks_every | 1h | 例行任务周期 |
[database] 持久层
| 键 | 实测值 | 说明 |
|---|---|---|
| class | mailman.database.sqlite.SQLiteDatabase | SQLite 引擎类 |
| url | sqlite:////opt/mailman/var/data/mailman.db | SQLAlchemy 连接串 |
| debug | no | SQL 调试日志开关 |
[mta] 收发与 MTA 对接(完整实测见 3.1 节输出)
| 键 | 实测值 | 说明 |
|---|---|---|
| incoming / outgoing | postfix.LMTP / deliver.deliver | 入站 LMTP、出站 SMTP 两条通道 |
| lmtp_host / lmtp_port | 127.0.0.1 / 8024 | Core 收信入口,须与 postfix transport 一致 |
| smtp_host / smtp_port | 127.0.0.1 / 25 | Core 出站交回 MTA |
| max_recipients | 10 | 单封收件人数上限(防爆破投递) |
| max_autoresponses_per_day | 10 | 自动回复每日上限 |
| delivery_retry_period | 5d | 投递失败重试窗口 |
| smtp_secure_mode / smtp_user / smtp_pass | smtp / 空 / 空 | 出站加密与认证(接外部中继时用) |
| verp_* 系列 | 见 5.3 | VERP 退信精确归因组 |
[webservice] REST API
| 键 | 实测值 | 说明 |
|---|---|---|
| hostname / port | 127.0.0.1 / 8001 | REST 监听,只绑本机 |
| admin_user / admin_pass | restadmin / q1w2e3r4 | REST 基本认证凭据 |
| api_version | 3.1 | API 版本 |
| workers | 2 | gunicorn worker 数 |
| use_https | no | REST 是否 TLS(本地通道保持 no) |
[paths.custom] 路径布局(var_dir 派生全部子目录)
| 键 | 实测值 |
|---|---|
| var_dir | /opt/mailman/var |
| etc_dir / log_dir | $var_dir/etc / $var_dir/logs |
| queue_dir | $var_dir/queue |
| data_dir / list_data_dir | $var_dir/data / $var_dir/lists |
| pid_file / lock_file | $var_dir/master.pid / $var_dir/locks/master.lck |
[archiver.*] 归档
| 键 | 实测值 | 说明 |
|---|---|---|
| [archiver.hyperkitty] class | mailman_hyperkitty.Archiver | 独立插件类(非 core 内置) |
| [archiver.hyperkitty] enable | yes | 归档总开关 |
| [archiver.hyperkitty] configuration | /etc/mailman3/mailman-hyperkitty.cfg | 插件配置(base_url + api_key) |
| [archiver.mail_archive] / [archiver.mhonarc] enable | no | 其他归档后端默认关 |
[runner.*] 队列引擎(每队列实例数/睡眠/自启,实测样例)
[runner.archive] instances: 1 sleep_time: 1s start: yes
[runner.bad] instances: 1 sleep_time: 1s start: no # 伪 runner,默认禁用
五、核心参数分类详解(每项「改 → 生效 → 验证」闭环)
5.1 [webservice] REST 凭据:配错即 401(实测闭环)
REST 凭据是 Core 与 Web 面的唯一管理通道口令。实验:把 mailman.cfg 的 admin_pass 改成 wrongpass 并重启,用旧凭据访问:
curl -s -u restadmin:q1w2e3r4 -w "\nHTTP=%{http_code}\n" http://127.0.0.1:8001/3.1/system/versions
{"title": "401 Unauthorized", "description": "REST API authorization failed"}
HTTP=401
改回 admin_pass: q1w2e3r4 重启后复测:
{"mailman_version": "GNU Mailman 3.3.10 (Tom Sawyer)", "python_version": "3.10.12 (main, Aug 31 2026, 10:18:17) [GCC 11.4.0]", "api_version": "3.1", "self_link": "http://127.0.0.1:8001/3.1/system/versions", "http_etag": "\"7a0313783b989609a9edd7b586965908b518385e\""}
HTTP=200
⚠️ 双端一致性:改
[webservice] admin_pass后,settings.py的MAILMAN_REST_API_PASS必须同步改,否则 Postorius/HyperKitty 全站报「与 Mailman 通信失败」。生产改法:两端同值、restart 两个服务、curl 验证 200。
5.2 [archiver.hyperkitty] enable:归档总开关(实测闭环)
改为 enable: no → 重启 Core → 发一封信,mailman.log 的 HyperKitty archived 行不增加;改回 yes 再发一封,立即归档:
sudo -u mailman /opt/mailman/venv/bin/mailman conf -k enable -s archiver.hyperkitty
实测计数证据:
关闭归档后 archived 行数: 3 -> 3 # enable: no,发信后行数不变
恢复归档后 archived 行数: 4 # enable: yes,新信立即归档
Oct 21 04:40:45 2026 (7721) HyperKitty archived message <20261021044042.B835E158BE97@list.example.org> to https://list.example.org/archives/list/test@list.example.org/message/4ORUMMRPGDZD7WS2RCXJKZF2RTJ5KLU7/
💡 说明:
enable: no期间消息正常投递(讨论不受影响),只是不进归档——适合「临时停归档」场景。归档打不开多半是另一回事(插件缺装/密钥错 → 消息 SHUNT,见 FAQ)。
5.3 [mta] lmtp_port:端口错位故障复现(实测闭环)
Core LMTP 与 postfix transport 是一对必须严格对齐的端点。实验:把 lmtp_port 改成 8025(postfix transport 仍指 8024),重启后端口确实换了,但入站信全部被 postfix 排队:
ss -ltn | grep -E '802[45]'
LISTEN 0 100 127.0.0.1:8025 0.0.0.0:*
发信后 postfix 日志(/var/log/mail.log):
Oct 21 04:41:02 list postfix/lmtp[7705]: connect to 127.0.0.1[127.0.0.1]:8024: Connection refused
Oct 21 04:41:02 list postfix/lmtp[7705]: 75F5B158BEBE: to=<test@list.example.org>, relay=none, delay=0, delays=0/0/0/0, dsn=4.4.1, status=deferred (connect to 127.0.0.1[127.0.0.1]:8024: Connection refused)
改回 lmtp_port: 8024 重启即恢复(deferred 队列会自动重投)。ss 显示 8024 重新监听:
LISTEN 0 100 127.0.0.1:8024 0.0.0.0:*
[mta] 其余要点:
verp_delivery_interval: 0(默认关闭逐封 VERP):改为 1 则每封出站信都用 VERP 变体地址投递(verp_format: ${bounces}+${local}=${domain}),退信可精确归因到单个成员,代价是每封单独投递;max_recipients: 10:LMTP 收信时 RCPT 数超限即走 moderation(防群发注入),命中会出现在 held 请求的X-Mailman-Rule-Hits: max-recipients头(规则集见 5.6 实验);smtp_user/smtp_pass/smtp_secure_mode:Core 出站走外部中继(如云厂商 relayhost)时的认证与 STARTTLS 配置。
5.4 [database] 与 [paths.custom]:换库与搬家
SQLite 适合单机小站;换 PostgreSQL 只改两行(以 Debian/Ubuntu 包 postgresql 与 python3-psycopg2 已装为前提):
[database]
class: mailman.database.sqlatabase.PostgreSQLDatabase
url: postgresql://mailman:q1w2e3r4@127.0.0.1/mailman
⚠️ 换库不迁移数据:新库是空库,列表/成员需重建或用
mailman import迁移;Web 面数据库(HyperKitty 归档)是另一套 Django 配置(settings.py DATABASES),互不相干。
[paths.custom] var_dir 指向哪,队列、日志、数据库、锁就都在其下派生(实测 5.x 派生关系见第四章总览表)。搬家 = 改 var_dir + 迁移目录 + 重启。
5.5 [mailman] 站点参数(site_owner 实测闭环)
把 site_owner 从 admin@example.org 改为 owner@list.example.org:
sudo -u mailman /opt/mailman/venv/bin/mailman conf -k site_owner
[mailman] site_owner: owner@list.example.org
行为的可观察面:待审请求触发时 Core 给 site_owner 发 OWNER 通知(本文 5.6 实验的 OWNER: <...> 日志行对应邮件投递),站点级系统邮件也发往该地址。改回 admin@example.org 同法验证。
5.6 列表级参数:max_message_size 超限即待审(实测闭环)
邮件体积上限不在全局配置里,而是每个列表一个属性(REST 可查改)。实测:默认 40 KB,改成 1 KB 后发一封约 2.3 KB 的信:
curl -s -u restadmin:q1w2e3r4 "http://127.0.0.1:8001/3.1/lists/test.list.example.org/config" \
| python3 -c 'import sys,json; print("默认 max_message_size:", json.load(sys.stdin)["max_message_size"], "KB")'
curl -s -u restadmin:q1w2e3r4 -X PATCH -d "max_message_size=1" \
"http://127.0.0.1:8001/3.1/lists/test.list.example.org/config" -o /dev/null -w "PATCH HTTP=%{http_code}\n"
默认 max_message_size: 40 KB
PATCH HTTP=204
发信后消息没有被拒收或丢弃,而是进入待审(held),规则命中直接写在消息头里(REST held 接口实测节选):
X-Mailman-Rule-Hits: max-size
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; news-moderation; no-subject; digests; suspicious-header
同时 mailman.log 出现给站长的通知行、held 接口 total_size 变 1:
Oct 21 04:44:30 2026 (7798) OWNER: <179255786875.7798.13827231093267562989@list.example.org>
处置与恢复(实测):
curl -s -u restadmin:q1w2e3r4 -X POST -d "action=discard" \
"http://127.0.0.1:8001/3.1/lists/test.list.example.org/held/1" -o /dev/null -w "POST HTTP=%{http_code}\n"
curl -s -u restadmin:q1w2e3r4 "http://127.0.0.1:8001/3.1/lists/test.list.example.org/held" \
| python3 -c 'import sys,json; print("剩余 held:", json.load(sys.stdin)["total_size"])'
POST discard HTTP=204
剩余 held: 0
💡 说明:
X-Mailman-Rule-Hits/Misses这一对头是排查「信为什么没过」的金钥匙——15 条 moderation 规则命中哪条一目了然(max-size/max-recipients/member-moderation/dmarc-mitigation 等)。处置动作是POST action=approve|discard|reject|defer,DELETE 不适用。
5.7 Web 面 settings.py:ALLOWED_HOSTS 白名单(实测闭环)
把 list.example.org 从 ALLOWED_HOSTS 移除并重启 mailman-web,带该 Host 头访问:
curl -s -o /dev/null -w "HTTP=%{http_code}\n" -H "Host: list.example.org" http://127.0.0.1/postorius/lists/
HTTP=400
恢复白名单后复测:
HTTP=200
Django 对白名单外 Host 一律 400(DisallowedHost)。其余 Web 面关键参数的联动关系(凭据/密钥对齐)在 5.1 与 5.2 节已闭环验证:MAILMAN_REST_API_PASS ↔ mailman.cfg admin_pass;MAILMAN_ARCHIVER_KEY ↔ mailman-hyperkitty.cfg api_key;ACCOUNT_DEFAULT_HTTP_PROTOCOL 未配 HTTPS 前必须 http。
六、生产配置模板
小站模板(≤10 列表 / ≤5k 成员,4 GB 内存、NVMe、SQLite 单机)——本文实测配置即此形态:
[mailman]
layout: custom
site_owner: owner@example.org
[paths.custom]
var_dir: /opt/mailman/var
[database]
class: mailman.database.sqlite.SQLiteDatabase
url: sqlite:////opt/mailman/var/data/mailman.db
[mta]
incoming: mailman.mta.postfix.LMTP
outgoing: mailman.mta.deliver.deliver
lmtp_host: 127.0.0.1
lmtp_port: 8024
smtp_host: 127.0.0.1
smtp_port: 25
configuration: /opt/mailman/etc/postfix-mailman.cfg
[webservice]
hostname: 127.0.0.1
port: 8001
admin_user: restadmin
admin_pass: <openssl rand -hex 16 生成>
[archiver.hyperkitty]
class: mailman_hyperkitty.Archiver
enable: yes
configuration: /etc/mailman3/mailman-hyperkitty.cfg
中站模板(10–100 列表,16 GB 内存,PostgreSQL + 独立归档机)——在上述基础上改:
[database]
class: mailman.database.postgresql.PostgreSQLDatabase
url: postgresql://mailman:强密码@db.internal:5432/mailman
[mta]
verp_delivery_interval: 1
max_delivery_threads: 4
并同步:settings.py 的 DATABASES 指向 PostgreSQL;HyperKitty 部署到独立主机时 mailman-hyperkitty.cfg 的 base_url 指向归档机;Core 与 Web 面凭据保持两端一致。
七、常见问题(FAQ)
Q1:改了 mailman.cfg 不生效。
先查查找顺序遮蔽(./var/etc/mailman.cfg、/var/etc/mailman.cfg 是否存在,存在即删);再确认重启了 mailman3 服务;最后用 mailman conf -k <键> 看生效值(第三章闭环)。
Q2:Postorius 报「无法连接 Mailman REST API」或 401。
settings.py 的 MAILMAN_REST_API_PASS 与 mailman.cfg [webservice] admin_pass 不一致(5.1 节实测 401 现象)。两端同值并分别重启。
Q3:消息被 SHUNT,日志 ModuleNotFoundError: No module named 'mailman.archiver(s)'。
归档器不在 Core 包内:pip install mailman-hyperkitty,class 必须写 mailman_hyperkitty.Archiver,并给出 configuration 指向插件配置;重启后 mailman unshunt 回收(安装篇 7.5 节完整实录)。
Q4:外站发信全 deferred,postfix 日志 Connection refused (port 8024)。
[mta] lmtp_port 与 /etc/postfix/transport 的 LMTP 端口错位(5.3 节实测复现)。两端对齐后 deferred 队列自动重投。
Q5:列表消息被莫名扣住。
看消息的 X-Mailman-Rule-Hits 头(5.6 节实测:max-size 命中即 held),再到 Postorius「待审请求」页或 REST held 接口处置;全局兜底调 [mta] max_recipients 与列表级 max_message_size。
Q6:Web 面访问 400 DisallowedHost。
settings.py 的 ALLOWED_HOSTS 缺该域名(5.7 节实测 400),补入域名保留 localhost/127.0.0.1,重启 mailman-web。