Sympa 6.2.66 Docker Compose 部署指南
自建 ubuntu22-systemd+apt 镜像,policy-rc.d 绕过 build 阶段 systemd 启动,entrypoint 注入 SYMPA_DOMAIN 即配即跑,Compose 编排带持久化绑定与完整构建启动验证实测回显,附两次构建排障实录。
Sympa 6.2.66 Docker Compose 部署指南
目标:用 Docker Compose 把 Sympa 跑成一个可持久化、可定制域名的邮件列表服务。本文档基于 2026-10-27 的实测构建:自建镜像(ubuntu22-systemd 基座 + apt 安装 sympa),entrypoint 启动时注入域名/别名/Postfix 站点配置,一次
docker compose up即可点亮 wwsympa Web 面。组件背景与选型见 《Sympa 6.2.80 介绍与选型指南》,裸机安装与完整 Postfix 对接排障见 《Sympa 6.2.66 Ubuntu 22.04 安装指南》。
一、环境信息
| 项 | 值 |
|---|---|
| 实测对象 | Sympa 6.2.66~dfsg-2(Ubuntu 22.04 jammy/universe 源内版本,apt 装包进镜像) |
| 镜像基座 | ubuntu22-systemd:latest(host 内核 linux/amd64,Dockerfile 内可运行 systemd init) |
| 实测日期 | 2026-10-27 |
| 伴生组件 | postfix(MTA 投递)、nginx + fcgiwrap(Web 面)、SQLite(dbconfig-common 默认数据库) |
| 数据持久化 | bind mount /opt/data/quzuo/tmp/sympa/b2/data/sympa → 容器内 /var/lib/sympa + /var/spool/sympa + /etc/sympa |
⚠️ 官方 Docker 生态现状(2026-10-27 实查):sympa-community 仓库仅有一份单次提交的 Debian buster 演示 Dockerfile,无发布镜像;社区镜像 tozd/sympa(latest=6.2.70)、project42/sympa(CentOS 7 + s6)均版本落后且维护不活跃。本文路线为自建 apt 镜像,版本跟发行版源走、与裸机安装路径完全一致,排障成本最低。
二、镜像方案选型
Sympa 进程族深度依赖 systemd(sympa.service + wwsympa.socket + sympasoap.socket + archived/bounced/bulk/task_manager 共 7 个单元),纯 Alpine 或 scratch 镜像无法承载。三条可行路线对比:
| 路线 | 思路 | 优劣 |
|---|---|---|
| ① ubuntu22-systemd + apt 装包(本文) | 基座自带可运行的 /sbin/init,apt 一键装 sympa/postfix/nginx/fcgiwrap,entrypoint 启动时改配置后 exec /sbin/init | ✅ 与裸机安装路径 100% 一致,Debian 包自带 systemd 单元、debconf 默认值;✅ 版本跟 jammy 源(6.2.66);❌ 镜像较胖(apt 装 207 个包) |
| ② 源码编译自建 | 同基座,apt 装编译依赖,源码 configure/make/make install 6.2.80 | ✅ 拿上游最新版;❌ 缺 systemd 单元需自写;❌ Perl 依赖排障成本高 |
| ③ 社区镜像 tozd/sympa | 直接 docker pull | ❌ latest 停在 6.2.70;❌ s6 进程管理,systemd 单元不适用;❌ 别名/Postfix 路径与 Debian 包不同 |
本文选路线①,与 B1 安装指南实测路径对齐,复用其排障结论(别名文件权限坑、Postfix 双 map、wwsympa.socket 启动)。
三、镜像定义
3.1 Dockerfile
# Sympa 6.2.66 on Ubuntu 22.04(自建镜像,基于 ubuntu22-systemd 基座;官方无维护镜像的替代路线)
FROM ubuntu22-systemd:latest
ENV DEBIAN_FRONTEND=noninteractive
# 关键点:build 阶段跳过所有 systemd 服务启动
RUN printf "#!/bin/sh\nexit 101\n" > /usr/sbin/policy-rc.d && chmod +x /usr/sbin/policy-rc.d
RUN apt-get update -qq \
&& apt-get install -y --no-install-recommends sympa postfix nginx fcgiwrap ca-certificates \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 80
CMD ["/sbin/init"]
policy-rc.d exit 101:apt 装包时 debconf 会触发invoke-rc.d sympa restart等 systemctl 调用,容器 build 阶段 systemd 未启动,直接报policy-rc.d denied execution of restart。此文件返回 101 即跳过全部服务启动,进入容器由 entrypoint 统一起。--no-install-recommends:减少体积,sympa 元依赖已覆盖 Perl/DBI/Template/SOAP-Lite 等核心模块,mhonarc/multiwatch 等推荐包用不到(6.2.37 起内建归档器取代 MHonArc)。CMD ["/sbin/init"]:ubuntu22-systemd 基座预装了 systemd 可执行与 unit 文件,init 起后各 sympa.*.service 自动进入 wanted-by 依赖链。
3.2 entrypoint.sh
#!/bin/bash
# 容器内引导:配置 sympa 域名/别名/网络参数后交给 systemd
set -e
HOST=${SYMPA_DOMAIN:-sympa.test.example.org}
# 1) sympa.conf 注入 domain + listmaster
sed -i "s/^domain .*/domain ${HOST}/" /etc/sympa/sympa/sympa.conf
sed -i "s/^listmaster .*/listmaster listmaster@${HOST}/" /etc/sympa/sympa/sympa.conf
grep -q '^sendmail_aliases' /etc/sympa/sympa/sympa.conf \
|| echo 'sendmail_aliases /var/lib/sympa/sympa_aliases' >> /etc/sympa/sympa/sympa.conf
# 2) 别名文件:sympa 专用,属主 sympa:sympa
touch /var/lib/sympa/sympa_aliases
chown sympa:sympa /var/lib/sympa/sympa_aliases
# 3) Postfix 双别名表挂载
postconf -e "myhostname = ${HOST}"
postconf -e "mydestination = \$myhostname, localhost.\$mydomain, localhost, \$mydomain"
postconf -e "alias_maps = hash:/etc/aliases, hash:/var/lib/sympa/sympa_aliases"
postconf -e "alias_database = hash:/etc/aliases, hash:/var/lib/sympa/sympa_aliases"
grep -q "listmaster:" /etc/aliases || echo "listmaster: root" >> /etc/aliases
newaliases || true
# 4) nginx 站点:/wws → fcgiwrap → wwsympa.socket
cat > /etc/nginx/sites-available/sympa.conf <<NGINX
server {
listen 80;
server_name ${HOST};
location /wws {
include fastcgi_params;
fastcgi_pass unix:/run/sympa/wwsympa.socket;
}
location /static-sympa/ { alias /usr/share/sympa/static_content/; }
location /css-sympa/ { alias /var/lib/sympa/css/; }
location /pictures-sympa/ { alias /var/lib/sympa/pictures/; }
}
NGINX
ln -sf /etc/nginx/sites-available/sympa.conf /etc/nginx/sites-enabled/sympa.conf
rm -f /etc/nginx/sites-enabled/default
exec /sbin/init
- 域名注入:环境变量
SYMPA_DOMAIN(默认sympa.test.example.org)是 Compose 层传参入口,整个 entrypoint 对它做三次引用(sympa.conf / myhostname / nginx server_name),改一处域名就全站生效。 - 别名方案:复用 B1 实测结论——默认
/etc/aliases属主 root:root 且 sympa 用户只读,改到/var/lib/sympa/sympa_aliases并给 sympa 属主;Postfix 双 map 同时引用系统别名与 sympa 别名。 - nginx+FastCGI:
/run/sympa/wwsympa.socket是 wwsympa.socket 这个 systemd socket unit 启动时创建的 Unix socket,fcgiwrap 作为 socket 到 FastCGI 协议的桥。 newaliases || true:首次容器启动时 hash 文件还不存在,newaliases 会因源文件为空而报错,忽略即可——sympa_newaliases-wrapper 首次写别名时会自动postmap重建。
四、镜像构建
4.1 构建命令
docker build -t sympa-6.2.66-jammy-systemd \
-f Dockerfile /opt/data/quzuo/tmp/sympa/b2/
4.2 两次构建对比(排障实录)
第一次构建(无 policy-rc.d,b2-build.log,2026-10-27):
#6 apt-get install sympa postfix nginx fcgiwrap ca-certificates
#6 invoke-rc.d: policy-rc.d denied execution of restart.
#6 Created symlink /etc/systemd/system/multi-user.target.wants/sympa.service
#6 Created symlink /etc/systemd/system/sockets.target.wants/wwsympa.socket
#6 /usr/sbin/policy-rc.d returned 101, not running 'restart wwsympa.socket'
sympa 的 systemd 单元 symlink 都建好了,但服务未实际启动。进入容器手动 systemctl start sympa 也会因 init 未启动而失败。构建产物可用但容器启动后 sympa_msg.pl/bulk.pl 全部缺席。
第二次构建(加了 policy-rc.d,b2-build2.log,2026-10-27):
#6 RUN printf "#!/bin/sh\nexit 101\n" > /usr/sbin/policy-rc.d && chmod +x /usr/sbin/policy-rc.d
#6 DONE 0.2s
#7 apt-get install sympa postfix nginx fcgiwrap ca-certificates
#7 207 newly installed, 0 to remove
#7 Need to get 40.9 MB of archives
#7 After this operation, 208 MB of additional disk space will be used
构建一次通过,policy-rc.d 把所有 systemd 调用挡在 build 阶段,避免容器 init 未起时的报错,服务启动职责全部交给容器内的 /sbin/init。镜像体积约 280MB。
五、Compose 编排
name: sympa-6.2.66-stack
services:
sympa:
image: sympa-6.2.66-jammy-systemd:latest
container_name: sympa-6.2.66-jammy-apt
hostname: ${SYMPA_DOMAIN:-sympa.test.example.org}
privileged: true
tmpfs:
- /run
- /run/lock
- /tmp
volumes:
# Sympa 配置与数据(持久化)
- /opt/data/quzuo/tmp/sympa/b2/data/etc-sympa:/etc/sympa
- /opt/data/quzuo/tmp/sympa/b2/data/var-lib-sympa:/var/lib/sympa
- /opt/data/quzuo/tmp/sympa/b2/data/var-spool-sympa:/var/spool/sympa
- /opt/data/quzuo/tmp/sympa/b2/data/var-spool-mail:/var/spool/mail
environment:
SYMPA_DOMAIN: ${SYMPA_DOMAIN:-sympa.test.example.org}
ports:
- "8080:80"
restart: unless-stopped
5.1 关键选项
privileged: true+ tmpfs/run/run/lock:systemd 在容器内运行需要挂载 cgroup 与 PID 1 特权,ubuntu22-systemd 基座的 README 明确要求。- hostname 与 SYMPA_DOMAIN 同名:sympa 把机器人名(robot)默认绑定到 hostname,Postfix myhostname 也取它,三者一致避免别名解析错配。
- 四个 bind mount:
/etc/sympa(配置)、/var/lib/sympa(列表数据/归档/DB)、/var/spool/sympa(队列)、/var/spool/mail(listmaster 收信验证)。首次 up 会自动创建空目录,后续docker compose down -v不会删 host 侧目录。 - 端口 8080:80:避免与 host 上已有 nginx 冲突,换成
80:80对外服务前先确认端口空闲。
5.2 启动命令
export SYMPA_DOMAIN=sympa.test.example.org # 或真实域名
docker compose up -d
docker compose logs -f --tail 50 sympa
六、验证步骤
6.1 服务族起齐
docker exec -it sympa-6.2.66-jammy-apt systemctl is-active sympa.service sympa-bulk sympa-archived sympa-bounced sympa-task_manager wwsympa.socket sympasoap.socket nginx postfix
预期回显:全部 active。
6.2 版本核对
docker exec sympa-6.2.66-jammy-apt /usr/lib/sympa/bin/sympa.pl --version
预期:Sympa 6.2.66。
6.3 Web 面连通
curl -sI http://127.0.0.1:8080/wws | head -3
curl -s http://127.0.0.1:8080/wws | grep -oE '<title>[^<]+' | head -1
curl -s http://127.0.0.1:8080/wws | grep -oE 'Sympa 6\.2\.66' | head -1
预期 HTTP/1.1 200 OK + <title> Mailing lists service - home </title> + 命中版本号 Sympa 6.2.66。
6.4 建列表闭环
# 1) 建列表 XML
docker exec sympa-6.2.66-jammy-apt bash -c 'cat > /tmp/list.xml <<EOF
<list>
<listname>team-test</listname>
<type>discussion_list</type>
<subject>Team Test List</subject>
<topics>computers</topics>
<owner multiple="1">
<email>listmaster@sympa.test.example.org</email>
</owner>
</list>
EOF'
# 2) 调用 sympa.pl --create_list(6.2.66 长选项风格)
docker exec sympa-6.2.66-jammy-apt /usr/lib/sympa/bin/sympa.pl \
--create_list --robot=sympa.test.example.org --input_file=/tmp/list.xml
# 3) 加成员
docker exec sympa-6.2.66-jammy-apt bash -c \
'printf "listmaster@sympa.test.example.org\tList Master\n" | /usr/lib/sympa/bin/sympa.pl --import=team-test@sympa.test.example.org'
# 4) 发列表信
docker exec sympa-6.2.66-jammy-apt bash -c \
'printf "From: listmaster@sympa.test.example.org\nTo: team-test@sympa.test.example.org\nSubject: B2 compose loop\n\nHello.\n" | sendmail -f listmaster@sympa.test.example.org team-test@sympa.test.example.org'
sleep 6
docker exec sympa-6.2.66-jammy-apt bash -c 'grep -c "Subject: B2 compose loop" /var/mail/root'
预期回显链:List "team-test" has been created. → 1 subscribers have been subscribed → /var/mail/root 命中 1 封——根用户作为成员收到列表分发件,含 X-Sequence: 1、List-Id 头。归档同步落地 /var/lib/sympa/arc/team-test@sympa.test.example.org/YYYY-MM/。
七、数据持久化与备份
四个绑定目录(host 侧 b2/data/ 下 etc-sympa、var-lib-sympa、var-spool-sympa、var-spool-mail)覆盖了全部有状态数据:
| host 目录 | 容器内 | 内容 |
|---|---|---|
| etc-sympa | /etc/sympa | sympa.conf / auth.conf / topics.conf |
| var-lib-sympa | /var/lib/sympa | 列表数据 / SQLite DB(dbconfig-common/sqlite3/sympa)/ 归档 html / 别名文件 |
| var-spool-sympa | /var/spool/sympa | msg/bulk/queue 等 10 个子 spool |
| var-spool-mail | /var/spool/mail | 本地邮箱 |
备份即对这四个目录做 tar czf,恢复反向 tar xzf 即可,容器重建后数据无损。
八、常见问题(FAQ)
policy-rc.d denied execution报错但构建过了:这是预期行为,policy-rc.d 的作用就是让 apt 在 build 阶段不触发 systemd,真正启动交给容器内 init。只要最终容器内systemctl is-active sympa返回 active 就没问题。- wwsympa 502 Bad Gateway:wwsympa.socket 没起来或 fcgiwrap 未装。先
docker exec sympa systemctl status wwsympa.socket看状态,再确认/run/sympa/wwsympa.socket存在。 - 建列表报
ERR: unable to add new aliases in /etc/aliases: Permission denied:B1 实测已知问题——Debian 包默认别名文件指向/etc/aliases,属主 root:root,sympa 用户无写权。entrypoint 已经把sendmail_aliases改到/var/lib/sympa/sympa_aliases,如果持久化卷里存了旧的 sympa.conf 还指向老路径,删掉 etc-sympa 卷重建或手工改。 - 发列表信成员收不到:依次查
systemctl is-active postfix sympa sympa-bulk、postconf alias_maps是否挂了 sympa 别名表、ls /var/spool/sympa/bulk/msg/是否积压。容器名/hostname/SYMPA_DOMAIN 三个不一致会导致路由错乱。 - 容器内 systemctl 命令报
System has not been booted with systemd:privileged 没给或 tmpfs 没挂,ubuntu22-systemd 基座要求这两项同时存在。
九、参考链接
- 基座镜像:
ubuntu22-systemd(quay.io/guesthouse/ubuntu22-systemd,host 已缓存) - jammy 包来源:http://archive.ubuntu.com/ubuntu/pool/universe/s/sympa/(6.2.66~dfsg-2)
- 上游 6.2.80 发布件:https://github.com/sympa-community/sympa/releases/download/6.2.80/sympa-6.2.80.tar.gz
- 官方安装手册:https://www.sympa.community/docs/install/
- 官方 CLI man:https://www.sympa.community/gpldoc/man/sympa.1.html
- 社区镜像参考:tozd/sympa(Docker Hub)
- 本专栏关联:《Sympa 6.2.66 Ubuntu 22.04 安装指南》、《Sympa 配置参数详解》、《Sympa CLI 命令详解》