Debian 12 安装 mlmmj 2.2.2 指南
Debian 12 从零安装 mlmmj 2.2.2 实测指南:apt/离线 deb/源码编译三方案对比、configure 报错排障实录、Postfix 别名管道与 recipient_delimiter 集成、列表目录属主坑位、maintd 托管与端到端投递验证。
Debian 12 安装 mlmmj 2.2.2 指南
目标:在一台 Debian 12 服务器上从零安装 mlmmj 2.2.2 邮件列表管理器,并完成与 Postfix 的对接(分隔符、别名管道、列表初始化、订阅与端到端投递验证)。全文命令与回显均在 Debian 12 (bookworm) x86_64 + Postfix 3.7.11 环境实测通过。
一、环境信息
本篇实测环境与关键产物路径如下,读者按同版本操作可直接复现。
- 操作系统:Debian 12 (bookworm),x86_64
- mlmmj 版本:2.2.2(源码编译,2026-10-09 发布,本篇主推方案);Debian 12 仓库打包版为 1.3.0-4(对比用)
- MTA:Postfix 3.7.11-0+deb12u1(Debian 12 官方源版本)
- 编译工具链:GCC 12.2.0(Debian 12 官方源 build-essential)
- 源码包:
RELEASE_2_2_2.tar.gz,291781 字节,sha2567c38047e681738cefc6e4eb2c13355e6d418cdacec20d39ad773167bb40fd047 - 关键路径:二进制
/usr/local/bin/mlmmj-*(源码方案)或/usr/bin/mlmmj-*(apt 方案);列表数据/var/spool/mlmmj/<列表名>/;手册页/usr/local/man/man1/;列表文本模板/usr/local/share/mlmmj/text.skel/ - 邮件域名约定:列表服务器主机名
lists.example.com,列表地址mlmmj-test@lists.example.com,测试订阅者alice@example.com/bob@example.com(与本机 Postfix 同域,便于本地投递验证) - 源码下载源说明:官方仓库在 Codeberg(codeberg.org/mlmmj/mlmmj),本篇下载直连官方
https://codeberg.org/mlmmj/mlmmj/archive/RELEASE_2_2_2.tar.gz实测可用(官方源直连即达,未发生降级)
二、前置条件
安装 mlmmj 前,确认服务器具备以下条件,缺一项都会导致后续集成步骤卡住。
- 一台可用的 MTA:mlmmj 自身不收发邮件,收信靠 MTA 别名管道触发、发信交给本机 SMTP 服务(默认
127.0.0.1:25)。本篇以 Postfix 为例;未安装的在方案 A 中会一并安装:
apt-get update
apt-get install -y postfix # MTA:收信管道与投递出口
- 编译依赖(仅源码方案需要):
apt-get install -y build-essential wget pkg-config # GCC/make、下载工具、pkg-config
- 主机名与邮件域规划:列表服务器的 FQDN(本例
lists.example.com)将出现在所有列表地址中,安装前确认 DNS/hosts 已就绪:
hostname -f
实测回显:
lists.example.com
- 确认 25 端口可用(MTA 投递出口依赖本机 SMTP):
ss -ltn | grep ':25 '
实测回显(Postfix 启动后):
LISTEN 0 100 0.0.0.0:25 0.0.0.0:* users:(("master",pid=5211,fd=13))
三、版本选型说明
mlmmj 的「仓库版」与「上游版」差距非常大,安装前必须先选型,这直接决定后续功能完整性。
| 渠道 | 版本 | 发布时间 | 说明 |
|---|---|---|---|
| Debian 12 官方源 | 1.3.0-4 | 上游 2016 年前后,包维护打包 | 与最新版相差两个大版本(1.x → 2.x) |
| 上游源码(Codeberg RELEASE_2_2_2) | 2.2.2 | 2026-10-09 | 默认注入 RFC 2919/2369 列表头、归档 YYYY/MM 分区、subrelease 自助释放、DKIM/From 改写变量等均为 2.x 能力 |
| Debian 12 仓库内 Postfix | 3.7.11-0+deb12u1 | 2024-03 | 与 mlmmj 无版本耦合,直接用仓库版即可 |
表格解读:1.3.0 与 2.2.2 的功能差距是实打实的——2.0.0 起默认生成的 List-Id/List-Unsubscribe 等标准头直接影响现代邮箱的投递与体验,1.3.0 全部没有。因此本篇推荐源码编译 2.2.2 为生产方案(方案 C),apt/离线包方案(A/B)适合仅作功能评估或内网受限场景。
⚠️ 版本自检陷阱:2.2.2 源码编译出的二进制执行
mlmmj-list -V会显示mlmmj-list version 2.2.1。原因是上游打 RELEASE_2_2_2 标签(2026-10-09 15:37)时未同步更新源码内版本常量(源码mk/defs.mk第 2 行仍是VERSION= 2.2.1,实查取证)。判断真实版本请以所下载源码标签为准,勿以-V输出为准。
四、方案 A:在线仓库安装(Debian 12 apt 源)
apt 方案是最省事的安装方式,两条命令完成;代价是拿到的是老版本 1.3.0。本节先给出完整安装过程与核验,末尾说明版本局限。
4.1 安装命令与实测输出
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y postfix mlmmj
实测回显(节选,保留 mlmmj 相关行):
Selecting previously unselected package mlmmj.
Unpacking mlmmj (1.3.0-4) ...
Setting up postfix (3.7.11-0+deb12u1) ...
Setting up mlmmj (1.3.0-4) ...
安装完成后核对包版本:
dpkg -l mlmmj postfix | tail -2
实测回显:
ii mlmmj 1.3.0-4 amd64 mail server independent mailing list manager
ii postfix 3.7.11-0+deb12u1 amd64 High-performance mail transport agent
4.2 安装产物与版本核验
apt 包把命令装到 /usr/bin/,并附带一个历史拼写兼容命令 mlmmj-recieve(多一个 c,指向与 mlmmj-receive 相同的实现,老脚本兼容用):
ls /usr/bin/mlmmj-*
实测回显:
/usr/bin/mlmmj-bounce
/usr/bin/mlmmj-list
/usr/bin/mlmmj-maintd
/usr/bin/mlmmj-make-ml
/usr/bin/mlmmj-process
/usr/bin/mlmmj-receive
/usr/bin/mlmmj-recieve
/usr/bin/mlmmj-send
/usr/bin/mlmmj-sub
/usr/bin/mlmmj-unsub
apt 方案还自带了配置联动:mlmmj 包安装时会登记 cron 维护任务与文档,Postfix 主配置无需额外改动即可进入第八章的集成流程。
4.3 apt 方案的版本局限
1.3.0-4 缺少 2.x 的全部新特性(默认 List-* 标准头、归档分区、自助释放、$disarmedfrom$ DKIM 配方等)。如果坚持用 apt 版本,注意它的建列表脚本提示的路径是 /usr/bin/,而后续维护任务命令同样用 /usr/bin/mlmmj-maintd;不要把 apt 版与源码版混装在同一台机器上不加区分地使用(PATH 中 /usr/local/bin 恒先于 /usr/bin,混装时实际执行的是源码版,见第十章)。
五、方案 B:deb 离线包安装(断网内网)
内网服务器无法访问 apt 源时,可在任意外网同架构机器上把 deb 包下载下来,用移动介质带入内网后 dpkg -i 安装。deb 包与方案 A 安装的是同一版本(1.3.0-4)。
第一步,在能联网的 Debian 12 机器上下载(只下载不安装):
mkdir -p /root/offline && cd /root/offline
apt-get download mlmmj postfix
实测回显:
Fetched 1714 kB in 2s (887 kB/s)
W: Download is performed unsandboxed as root as file '/root/offline/mlmmj_1.3.0-4_amd64.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)
说明:
_apt用户权限警告不影响下载结果,文件已正常落盘。若要消除该警告,可先chown _apt /root/offline或下载到公共可读目录。
第二步,核对包完整性与大小:
cd /root/offline && ls -l *.deb && sha256sum *.deb
实测回显:
-rw-r--r-- 1 root root 203716 Aug 27 2020 mlmmj_1.3.0-4_amd64.deb
-rw-r--r-- 1 root root 1510172 Mar 24 2024 postfix_3.7.11-0+deb12u1_amd64.deb
98d6d18bfe754dae49d34ac0c8936185b631b704f0223ab4f055643ea713d6a2 mlmmj_1.3.0-4_amd64.deb
a097fa379fb22c5dcb2b95446ec355fbea8aa338c185eb37685f8db87d68b305 postfix_3.7.11-0+deb12u1_amd64.deb
第三步,把两个 deb 带入内网目标机后离线安装:
cd /root/offline
dpkg -i mlmmj_1.3.0-4_amd64.deb postfix_3.7.11-0+deb12u1_amd64.deb
实测回显(dpkg -i 重装演示,节选):
(Reading database ... 11874 files and directories currently installed.)
Preparing to unpack mlmmj_1.3.0-4_amd64.deb ...
Unpacking mlmmj (1.3.0-4) over (1.3.0-4) ...
Setting up mlmmj (1.3.0-4) ...
⚠️ 离线安装的连带依赖:
dpkg -i不解析依赖,若目标机缺 postfix 依赖库(如 libsasl2、libdb 等),先在联网机上用apt-get download把apt-get install -s postfix模拟输出里列出的依赖包一并下载带入。安装后用dpkg -l mlmmj postfix核对为ii状态。
验证命令:
dpkg -l mlmmj | tail -1
实测回显:
ii mlmmj 1.3.0-4 amd64 mail server independent mailing list manager
六、方案 C:源码编译安装最新 2.2.2(生产推荐)
从官方 Codeberg 仓库直接编译最新稳定版,是拿到 2.x 完整能力的唯一途径(截至 2026-10-26,Debian 12 仓库尚无 2.x 打包)。全程约 3 分钟。
6.1 安装编译依赖
apt-get install -y build-essential wget pkg-config
实测回显(节选):
Setting up build-essential (12.9) ...
Setting up pkg-config (1.8.1-1) ...
用 gcc --version 核对工具链:
gcc --version | head -1
实测回显:
gcc (Debian 12.2.0-14+deb12u1) 12.2.0
6.2 下载源码包
源码统一放 /usr/local/src(生产服务器惯例路径),下载后立即校验 sha256:
cd /usr/local/src
wget https://codeberg.org/mlmmj/mlmmj/archive/RELEASE_2_2_2.tar.gz -O mlmmj-2.2.2.tar.gz
sha256sum mlmmj-2.2.2.tar.gz
实测回显:
--2026-10-26 08:05:47-- https://codeberg.org/mlmmj/mlmmj/archive/RELEASE_2_2_2.tar.gz
Resolving codeberg.org (codeberg.org)... 217.197.91.145
Connecting to codeberg.org (codeberg.org)|217.197.91.145|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/gzip]
Saving to: 'mlmmj-2.2.2.tar.gz'
mlmmj-2.2.2.tar.gz [ <=> ] 291.78K --.-KB/s in 0.1s
2026-10-26 08:05:47 (2.68 MB/s) - 'mlmmj-2.2.2.tar.gz' saved [291781]
7c38047e681738cefc6e4eb2c13355e6d418cdacec20d39ad773167bb40fd047 mlmmj-2.2.2.tar.gz
解压并进入源码目录:
tar xf mlmmj-2.2.2.tar.gz && cd mlmmj
⚠️ 不要把源码解压到
/tmp再在其中编译:部分服务器的/tmp挂载了noexec属性(或按安全基线配置了 noexec),./configure会以退出码 126 直接无法执行。/usr/local/src是无此限制的惯例位置。
6.3 configure 配置(含两个报错实录)
mlmmj 的 configure 是自研脚本(非 autoconf),先看可用开关:
./configure --help | grep -A8 'Project options'
实测回显:
Project options:
--with-asan build with libasan support
--with-lsan build with liblsan support
--with-ubsan build with libubsan support
--with-tsan build with libtsan support
--with-coverage build with coverage flags
--enable-receive-strip build mlmmj-receive-strip (default: disabled)
--disable-tests build and run tests (default: enabled)
直接执行 ./configure 会连续踩到两个报错,实录如下(读者遇到同样报错可直接对号入座):
第一次报错(缺 pkg-config):
Found C compiler: cc
Host: x86_64-unknown-linux
Configuring from /usr/local/src/mlmmj
Found tool: ar
Found tool: ranlib
Found tool: strip
Checking for function arc4random_uniform... yes
Checking for function copy_file_range... yes
Checking for iconv... yes
Error: Unable to find pkg-config (required for tests)
装上 pkg-config 后第二次报错(缺 atf-c 测试框架):
Checking for iconv... yes
Checking for package atf-c... no
Error: Unable to find atf-c via pkg-config (required for tests)
结论:测试套件需要 pkg-config + atf-c(Automated Testing Framework)。两种处理:apt-get install -y libatf-dev 补齐并保留测试,或加 --disable-tests 跳过测试(生产部署的常规选择)。本篇采用后者:
./configure --disable-tests --prefix=/usr/local
实测回显(完整):
Found C compiler: cc
Host: x86_64-unknown-linux
Configuring from /usr/local/src/mlmmj
Found tool: ar
Found tool: ranlib
Found tool: strip
Checking for function arc4random_uniform... yes
Checking for function copy_file_range... yes
Checking for iconv... yes
Creating config.h
Creating mk/defs.mk from mk/defs.mk.in
Creating Makefile from Makefile.in
Creating src/Makefile from src/Makefile.in
Creating listtexts/Makefile from listtexts/Makefile.in
Creating contrib/Makefile from contrib/Makefile.in
Creating contrib/receivestrip/Makefile from contrib/receivestrip/Makefile.in
Creating src/mlmmj-make-ml from src/mlmmj-make-ml.in
Creating kyua.conf from kyua.conf.in
Configuration complete. You can now run 'make'.
6.4 编译与安装
make -j2
make install
make 结尾回显(节选):
make[2]: Leaving directory '/usr/local/src/mlmmj/contrib/receivestrip'
make[1]: Leaving directory '/usr/local/src/mlmmj/contrib'
make install 无报错即完成(它同时安装二进制、man 手册页与列表文本模板)。
6.5 安装产物核验与版本自检
ls /usr/local/bin/ | tr '\n' ' '; echo
ls /usr/local/man/man1/ | tr '\n' ' '; echo
实测回显:
mlmmj-bounce mlmmj-list mlmmj-maintd mlmmj-make-ml mlmmj-make-ml.sh mlmmj-process mlmmj-receive mlmmj-recieve mlmmj-send mlmmj-sub mlmmj-unsub
mlmmj-bounce.1 mlmmj-list.1 mlmmj-maintd.1 mlmmj-make-ml.1 mlmmj-process.1 mlmmj-receive.1 mlmmj-send.1 mlmmj-sub.1 mlmmj-unsub.1
版本自检(注意第三章说明的 2.2.2 自报 2.2.1 陷阱):
which mlmmj-list
mlmmj-list -V
实测回显:
/usr/local/bin/mlmmj-list
mlmmj-list version 2.2.1
再核一处目录:多语言列表文本模板已就位(含 zh-cn,供建列表时选择):
ls /usr/local/share/mlmmj/text.skel
实测回显:
ast cs de en fi fr gr it pt sk zh-cn
七、方案对比与选择建议
三种方案在同一台 Debian 12 上的实测对比如下:
| 维度 | A:在线 apt | B:deb 离线包 | C:源码编译 |
|---|---|---|---|
| 安装版本 | 1.3.0-4 | 1.3.0-4 | 2.2.2(上游最新) |
| 需联网 | 是(apt 源) | 否(离线带入) | 是(拉源码)或离线带入源码包 |
| 安装路径 | /usr/bin | /usr/bin | /usr/local/bin |
| systemd/cron 联动 | 包自带 cron 任务 | 同 apt | 需自建(见第九章) |
| man 手册/文本模板 | /usr/share 下齐全 | 同 apt | /usr/local/man、/usr/local/share/mlmmj |
| 2.x 特性(List-* 头/归档分区/DKIM 配方) | 无 | 无 | 全部具备 |
| 适用场景 | 快速评估、特性兼容性验证 | 断网内网、合规审计环境 | 生产部署(本篇推荐) |
表格解读:能联网且要上生产,直接选 C;断网内网先评估 B(但拿不到 2.x 能力);A 最快但也最老,建议只在「先跑通流程理解概念」阶段使用。三个方案互斥性不强——A/B 装在 /usr/bin、C 装在 /usr/local/bin,共存时 PATH 优先执行 C 的版本,务必按第十章清理其一避免混乱。
八、Postfix 集成配置与列表初始化
mlmmj 装完只是拿到一堆命令,必须完成「分隔符 → 建列表 → 别名 → 属主权限」四步才算可用。本节以列表 mlmmj-test@lists.example.com 为例,全部命令实测。
8.1 Postfix 基础邮件域配置
先让本机 Postfix 认下「列表域」,并把投递出口准备好(以下值按你的实际域名替换):
postconf -e 'myhostname = lists.example.com'
postconf -e 'mydomain = example.com'
postconf -e 'myorigin = $mydomain'
postconf -e 'mydestination = $myhostname, $mydomain, localhost'
postconf -e 'mynetworks = 127.0.0.0/8'
postconf -e 'inet_interfaces = all'
systemctl restart postfix
systemctl is-active postfix
实测回显:
active
8.2 收件人扩展分隔符(关键项)
mlmmj 的全部邮件指令(订阅/退订/取信/帮助)都挂在 列表名+扩展词@域名 形式的地址上,分隔符默认 +,必须与 Postfix 一致:
postconf -e 'recipient_delimiter = +'
postconf myhostname mydestination recipient_delimiter
实测回显:
myhostname = lists.example.com
mydestination = $myhostname, $mydomain, localhost
recipient_delimiter = +
⚠️ 不要把分隔符改成
-:官方 README 明确注明「使用-作分隔符大概率不工作」,因为 mlmmj 自身把-用作内部子定界(如+bounces-1-),列表名里也不能带-。
8.3 建列表(mlmmj-make-ml)
用官方脚手架一条命令建列表(非交互参数:-L 列表名、-d 域名、-c 属主用户、-a 自动写别名、-s spool 根目录):
/usr/local/bin/mlmmj-make-ml -L mlmmj-test -d lists.example.com -c postfix -a -s /var/spool/mlmmj
实测回显(交互提示均取默认回车):
The emailaddress of the list owner? [postmaster@lists.example.com] :
You can choose between the following languages for the list texts.
Available languages:
ast
cs
de
en
fi
fr
gr
it
pt
sk
zh-cn
mailing list texts language? [en] :
If you're not starting mlmmj-maintd in daemon mode,
don't forget to add this line to your crontab:
0 */2 * * * /usr/local/bin/mlmmj-maintd -F -L /var/spool/mlmmj/mlmmj-test/
Should I add it to the system-wide crontab at /etc/crontab for you? (y/n) [n]
Mailing list /var/spool/mlmmj/mlmmj-test was created successfully.
** FINAL NOTES **
1) The mailinglist directory has to be owned by the user running the
mailserver (i.e. starting the binaries to work the list)
2) Run newaliases
核对生成的目录树与关键控制文件:
ls /var/spool/mlmmj/mlmmj-test/ | tr '\n' ' '; echo
cat /var/spool/mlmmj/mlmmj-test/control/listaddress /var/spool/mlmmj/mlmmj-test/control/owner
实测回显:
archive bounce control digesters.d incoming index moderation nomailsubs.d queue requeue subconf subscribers.d text unsubconf
mlmmj-test@lists.example.com
postmaster@lists.example.com
8.4 别名与 newaliases
-a 参数已把收信管道写入 /etc/aliases,核对内容:
tail -2 /etc/aliases
实测回显:
postmaster: root
mlmmj-test: "|/usr/local/bin/mlmmj-receive -L /var/spool/mlmmj/mlmmj-test/"
重建别名库(官方 FINAL NOTES 第 2 条,忘了这步 Postfix 找不到列表地址):
newaliases
8.5 列表目录属主(最容易踩的坑)
Postfix 的别名管道默认以 nobody(default_privs 的默认值)身份执行 mlmmj-receive,列表目录必须让 nobody 可写。上面 -c postfix 把目录属主设成了 postfix,与管道运行用户不符——若此时直接投递,邮件会静默滞留。
先看一个反面实录:如果试图用 default_privs = postfix 让管道直接跑 postfix 身份,Postfix 会拒绝:
newaliases: fatal: file /etc/postfix/main.cf: parameters default_privs and mail_owner specify the same user postfix
正确做法是把列表目录属主改成管道实际运行的用户 nobody:
postconf -X default_privs
chown -R nobody:nogroup /var/spool/mlmmj
newaliases
实测回显(无输出即成功,可用下述命令复核属主):
ls -ld /var/spool/mlmmj/mlmmj-test
实测回显:
drwxr-xr-x 13 nobody nogroup 4096 Oct 26 08:11 /var/spool/mlmmj/mlmmj-test
💡 说明:官方 README 也点名了这个坑——「有的 Postfix 安装以 postfix 用户运行,但写文件时用 nobody:nogroup」。规则一句话:列表目录属主 = 别名管道实际执行者 =
default_privs(默认 nobody)。后续建的每个新列表都要执行同样的 chown。
8.6 订阅与端到端投递验证
先创建两个本地测试用户作为订阅者(真实场景是远端邮箱,原理相同):
useradd -m -s /bin/bash alice
useradd -m -s /bin/bash bob
用管理员模式 -a 静默订阅两人(跳过邮件确认,适合测试与导入名单):
/usr/local/bin/mlmmj-sub -L /var/spool/mlmmj/mlmmj-test/ -a alice@example.com
/usr/local/bin/mlmmj-sub -L /var/spool/mlmmj/mlmmj-test/ -a bob@example.com
实测回显(两条一致):
Changing to uid 65534 gid 65534, owner of /var/spool/mlmmj/mlmmj-test/.
列出订阅者核对:
/usr/local/bin/mlmmj-list -L /var/spool/mlmmj/mlmmj-test/
实测回显:
bob@example.com
alice@example.com
向列表投一封信(发件人 alice,走本机 Postfix 的 sendmail 入口):
printf 'From: alice@example.com\nTo: mlmmj-test@lists.example.com\nSubject: hello list\n\nHello everyone, first post via sendmail pipe test.\n' \
| sendmail -f alice@example.com mlmmj-test@lists.example.com
sleep 5
验证 alice 与 bob 的邮箱都收到了列表分发版:
head -25 /var/mail/bob
实测回显(关键头完整保留):
From mlmmj-test+bounces-1-bob=example.com@lists.example.com Mon Oct 26 08:07:21 2026
Return-Path: <mlmmj-test+bounces-1-bob=example.com@lists.example.com>
X-Original-To: bob@example.com
Delivered-To: bob@example.com
Received: from lists.example.com (localhost [127.0.0.1])
by lists.example.com (Postfix) with ESMTP id 760A5388320
for <bob@example.com>; Mon, 26 Oct 2026 08:07:21 +0000 (UTC)
X-Original-To: mlmmj-test@lists.example.com
Delivered-To: mlmmj-test@lists.example.com
Received: by lists.example.com (Postfix, from userid 0)
id 6CCEB38831B; Mon, 26 Oct 2026 08:07:09 +0000 (UTC)
From: alice@example.com
To: mlmmj-test@lists.example.com
Subject: hello list
Message-Id: <20261026080709.6CCEB38831B@lists.example.com>
Date: Mon, 26 Oct 2026 08:07:09 +0000 (UTC)
List-Id: <mlmmj-test.lists.example.com>
List-Post: <mailto:mlmmj-test@lists.example.com>
List-Help: <mailto:mlmmj-test+help@lists.example.com>
List-Subscribe: <mailto:mlmmj-test+subscribe@lists.example.com>
List-Unsubscribe: <mailto:mlmmj-test+unsubscribe@lists.example.com>
List-Owner: <mailto:mlmmj-test+owner@lists.example.com>
Precedence: list
逐项解读这封信证明的事实:信封地址是 VERP 形式(mlmmj-test+bounces-1-bob=example.com@,退信自动定位到 bob);List-Id 等 7 个标准列表头由 mlmmj 2.x 默认注入;Precedence: list 告知自动回复器勿回信。到此 mlmmj 的「收 → 审 → 发 → 退信跟踪」主链路全部打通。
九、维护任务 mlmmj-maintd 托管
mlmmj 的退信处理、队列重放、摘要发送、过期清理全部由 mlmmj-maintd 完成,必须为每个列表(或每个 spool 根目录)配置周期维护。两种托管方式二选一。
9.1 systemd 服务方式(推荐常驻托管)
创建服务单元(-d 参数让 maintd 扫描整个 spool 根目录下所有列表,守护模式自 daemonize):
cat > /etc/systemd/system/mlmmj-maintd.service <<'EOF'
[Unit]
Description=mlmmj mailing list maintenance daemon
After=network.target postfix.service
[Service]
Type=forking
ExecStart=/usr/local/bin/mlmmj-maintd -d /var/spool/mlmmj
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now mlmmj-maintd
实测回显:
Created symlink /etc/systemd/system/multi-user.target.wants/mlmmj-maintd.service → /etc/systemd/system/mlmmj-maintd.service.
状态核验(注意 244KB 的内存占用,印证 mlmmj 的轻量定位):
systemctl status mlmmj-maintd --no-pager -l | head -10
实测回显:
● mlmmj-maintd.service - mlmmj mailing list maintenance daemon
Loaded: loaded (/etc/systemd/system/mlmmj-maintd.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-10-26 08:14:21 UTC; 2s ago
Process: 7587 ExecStart=/usr/local/bin/mlmmj-maintd -d /var/spool/mlmmj (code=exited, status=0/SUCCESS)
Main PID: 7588 (mlmmj-maintd)
Tasks: 1 (limit: 19553)
Memory: 244.0K
CPU: 2ms
CGroup: /system.slice/mlmmj-maintd.service
└─7588 /usr/local/bin/mlmmj-maintd -d /var/spool/mlmmj
维护动作的证据落在每个列表目录的 mlmmj-maintd.lastrun.log:
cat /var/spool/mlmmj/mlmmj-test/mlmmj-maintd.lastrun.log
实测回显(一次维护运行的十个阶段):
Starting maintenance run at Mon Oct 26 08:14:21 2026
clean_moderation
clean_discarded
clean_subconf
clean_unsubconf
resend_queue
resend_requeue
clean_nolongerbouncing
unsub_bouncers
probe_bouncers
run_digests
9.2 cron 方式(官方推荐写法)
官方 README 目前推荐的仍是 cron 每 2 小时一次(对低流量列表足够,且天然避免常驻进程)。选 cron 方式时先停掉上面的 systemd 服务(systemctl disable --now mlmmj-maintd),再把官方提示的行写入 crontab——单列表逐行写,多列表用 -d 一行覆盖:
# 单列表:/etc/crontab 追加一行
0 */2 * * * root /usr/local/bin/mlmmj-maintd -F -L /var/spool/mlmmj/mlmmj-test
# 多列表:整个 spool 根目录一行搞定
0 */2 * * * root /usr/local/bin/mlmmj-maintd -F -d /var/spool/mlmmj
⚠️ 注意
-F:cron 模式必须带-F(前台运行一次即退出),systemd 守护模式则用不带-F的-d。两条路写混会出现双实例并行维护,可能对同一队列重复处理。
十、升级、降级与多版本共存
- 源码版升级:下载新版本标签的 tar 包,解压到
/usr/local/src,重复第六章 configure/make/make install——同一--prefix直接覆盖旧二进制,列表数据在/var/spool/mlmmj与安装目录无关,升级不影响列表。升级后跑一次mlmmj-maintd -F -d /var/spool/mlmmj并检查 lastrun 日志确认新版本工作正常。 - 降级:源码安装无版本管理,降级 = 下载旧标签包重新 make install 覆盖;或
apt-get install mlmmj装回 1.3.0(注意此时/usr/bin与/usr/local/bin并存)。 - 多版本共存判定:
which mlmmj-list显示实际执行的是/usr/local/bin(PATH 中 /usr/local/bin 恒在前)。生产建议二选一:用源码版就apt-get purge mlmmj移除仓库版,避免两套版本的 maintd/cron 各跑一份。
十一、常见问题(FAQ)
Q1:./configure 报 Unable to find pkg-config (required for tests)?
缺 pkg-config,apt-get install -y pkg-config 后重跑;若接着报 Unable to find atf-c via pkg-config (required for tests),要么 apt-get install -y libatf-dev 补测试框架,要么 ./configure --disable-tests 跳过测试(见 6.3 节实录)。
Q2:./configure 退出码 126、无法执行?
检查源码所在分区是否 noexec 挂载(mount | grep $(df --output=target /usr/local/src | tail -1) 看挂载项),把源码移到无 noexec 的目录(如 /usr/local/src)再编译。
Q3:newaliases 报 parameters default_privs and mail_owner specify the same user postfix?
default_privs 不能与 mail_owner(postfix)相同。不要改 default_privs,保持默认 nobody,把列表目录 chown 给 nobody:nogroup(见 8.5 节)。
Q4:投递后列表没反应、订阅者收不到信?
按链路排查三处:① cat /var/mail/服务账号 或 mailq 看 Postfix 是否把信交给了管道;② ls -l /var/spool/mlmmj/mlmmj-test/incoming/ 有没有落件——有落件说明管道通,问题在 process 阶段(多为目录属主不对,回看 8.5 节);③ tail /var/log/mail.log 找 mlmmj 相关报错。
Q5:sendmail: warning: unable to look up public/pickup: No such file or directory?
Postfix 刚启动/重启后队列管理目录尚未就绪时 sendmail 入队会打此警告,多数情况下信件仍会入队。规范做法是等 Postfix 完全启动(systemctl is-active postfix 为 active 且 /var/spool/postfix/public/pickup 存在)再投信;若警告后信确实没进队列,systemctl restart postfix 后重投。
Q6:mlmmj-list -V 显示的版本和下载的源码版本对不上?
上游 2.2.2 标签未更新源码内版本常量,二进制自报 2.2.1 属正常现象(见第三章版本选型说明),以你实际下载的 Release 标签为准。
Q7:订阅者发信后收到一封 "you are not subscribed" 拒绝信?
列表开启了 subonlypost(仅订阅者可发帖)而发件人的 From 地址与订阅地址不一致。让订阅者用订阅地址发信,或按官方 DKIM 配方在 control/customheaders 里规划 From 改写(见配置参数详解篇 4.3 节同款控制文件说明)。
十二、卸载与清理
源码方案卸载(make install 无对应 uninstall 目标,手动删除三类产物):
rm -f /usr/local/bin/mlmmj-*
rm -f /usr/local/man/man1/mlmmj-*.1
rm -rf /usr/local/share/mlmmj
rm -f /etc/systemd/system/mlmmj-maintd.service
systemctl daemon-reload
apt/离线方案卸载:
apt-get purge -y mlmmj
apt-get autoremove -y
数据清理(卸载软件不会动列表数据,确认不再保留后手动删):
rm -rf /var/spool/mlmmj
# 同时移除 /etc/aliases 中的 mlmmj-* 行并执行 newaliases
清理后核验:ls /usr/local/bin/ | grep mlmmj、dpkg -l | grep mlmmj、ls /var/spool/ | grep mlmmj 均应无输出。
十三、参考链接
- 官方安装文档(README):https://mlmmj.org/README.html
- Postfix 对接专篇(源码包 README.postfix.md):https://mlmmj.org/README.postfix.html
- 版本发布页:https://codeberg.org/mlmmj/mlmmj/releases
- 2.2.2 源码包直链:https://codeberg.org/mlmmj/mlmmj/archive/RELEASE_2_2_2.tar.gz
- Exim4/Qmail/Sendmail 对接说明(源码包内 README.exim4.md / README.qmail.md / README.sendmail.md)
- 本专栏认知篇:介绍与选型指南(含 2.x 功能演进与同类对比)
- 本专栏后续篇目:Compose 编排部署篇(自建部署形态)、配置参数详解、常用 CLI 命令与工具详解(发布后从专栏目录进入)