EmailRelay 2.6.1 Docker Compose 部署指南
EmailRelay 2.6.1 Docker Compose 部署指南:官方无镜像前提下的两阶段 Dockerfile 自建镜像(201MB)、relay+sink 双服务编排、as-proxy 宏隐含 --close-stderr 导致日志不可见的显式展开解法、8131/8132 端口端到端 swaks 与管理口实测。实测日期 2026-10-25。
EmailRelay 2.6.1 Docker Compose 部署指南
目标:用两阶段 Dockerfile 自建 EmailRelay 2.6.1 镜像,并以 Compose 编排「relay(存储转发)+ sink(模拟上游)」双服务完成端到端验证。已在 Docker 环境实测通过,实测日期 2026-10-25。
一、环境信息
- 镜像基础:ubuntu:22.04
- 软件版本:EmailRelay 2.6.1(源码包
emailrelay-2.6.1-src.tar.gz,SHA256a0bcd88179a6545817b670b1ed6d6aac7c4dae90fabd220ef08d3f6743b69e9c) - 镜像体积:201MB(两阶段构建后)
- 宿主端口规划:8131→relay 的 SMTP(25),8132→relay 的管理口(10027)
- 编排项目名:
emailrelay-2.6.1-stack;内网网段 10.89.72.0/24
二、镜像选型说明
Docker Hub 实查:EmailRelay 官方不发布任何容器镜像,也无维护活跃的社区镜像,因此本文采用「ubuntu:22.04 + 官方源码包编译」的自建镜像——版本与《EmailRelay 2.6.1 Ubuntu 22.04 安装指南》完全一致,升级只需改 ARG EMAILRELAY_VERSION。
三、前置条件
- Docker Engine 与 Docker Compose v2 可用;
- 宿主 8131/8132 端口空闲(
ss -ltn | grep -E '8131|8132'无输出); - 能访问 downloads.sourceforge.net(构建期下载源码包)。
四、部署步骤
4.1 项目结构
emailrelay-compose/
├── Dockerfile
└── docker-compose.yml
4.2 Dockerfile(两阶段:编译 → 运行时)
# syntax=docker/dockerfile:1
FROM ubuntu:22.04 AS build
ARG EMAILRELAY_VERSION=2.6.1
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential wget ca-certificates libssl-dev libpam0g-dev \
&& rm -rf /var/lib/apt/lists/*
RUN cd /tmp \
&& wget -q https://downloads.sourceforge.net/project/emailrelay/emailrelay/${EMAILRELAY_VERSION}/emailrelay-${EMAILRELAY_VERSION}-src.tar.gz \
&& tar -xzf emailrelay-${EMAILRELAY_VERSION}-src.tar.gz \
&& cd emailrelay-${EMAILRELAY_VERSION} \
&& ./configure --prefix=/usr/local \
&& make -j"$(nproc)" \
&& make install DESTDIR=/pkg
FROM ubuntu:22.04
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
libssl3 libpam0g ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /pkg/usr/local /usr/local
RUN mkdir -p /var/spool/emailrelay
EXPOSE 25
VOLUME ["/var/spool/emailrelay"]
ENTRYPOINT ["/usr/local/sbin/emailrelay"]
CMD ["--as-server", "--port", "25", "--spool-dir", "/var/spool/emailrelay", \
"--remote-clients", "--log", "--log-time", "--no-daemon", "--user", "root"]
要点:构建阶段装编译链与 libssl-dev/libpam0g-dev(缺了就没有 TLS/PAM 能力),make install DESTDIR=/pkg 把产物收进独立目录;运行阶段只带 libssl3/libpam0g 运行库,COPY 成品,镜像 201MB。
4.3 docker-compose.yml
name: emailrelay-2.6.1-stack
networks:
emailrelay-net:
driver: bridge
ipam:
config:
- subnet: 10.89.72.0/24
services:
relay:
build: .
image: emailrelay:2.6.1
container_name: emailrelay-2.6.1-relay
hostname: mail-relay.internal
networks:
- emailrelay-net
command:
- "--log"
- "--log-time"
- "--verbose"
- "--no-syslog"
- "--forward-on-disconnect"
- "--forward-to"
- "sink:10026"
- "--port"
- "25"
- "--spool-dir"
- "/var/spool/emailrelay"
- "--remote-clients"
- "--no-daemon"
- "--user"
- "root"
- "--admin"
- "10027"
ports:
- "8131:25"
- "8132:10027"
volumes:
- relay-spool:/var/spool/emailrelay
sink:
image: emailrelay:2.6.1
container_name: emailrelay-2.6.1-sink
hostname: upstream-sink.internal
networks:
- emailrelay-net
command:
- "--log"
- "--log-time"
- "--verbose"
- "--no-syslog"
- "--port"
- "10026"
- "--spool-dir"
- "/var/spool/emailrelay"
- "--remote-clients"
- "--no-daemon"
- "--user"
- "root"
volumes:
- sink-spool:/var/spool/emailrelay
volumes:
relay-spool:
sink-spool:
⚠️ 关键写法:relay 没有直接用
--as-proxy sink:10026、sink 没有直接用--as-server——这两个宏选项都隐含--close-stderr(启动后关闭 stderr,日志只进 syslog),容器里没有 syslog 守护,docker logs会一直空白。上表把它们展开为等价显式参数(--as-proxy=--log --close-stderr --forward-on-disconnect --forward-to,去掉--close-stderr)并补--no-syslog,日志即可经 stderr 被docker logs/docker compose logs捕获。sink 侧--as-server=--log --close-stderr,展开后不加转发参数即为纯存储守护。
4.4 构建并启动
cd emailrelay-compose
docker compose build
docker compose up -d
docker compose ps
docker compose ps 实测回显:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
emailrelay-2.6.1-relay emailrelay:2.6.1 "/usr/local/sbin/ema…" relay 14 seconds ago Up 14 seconds 0.0.0.0:8131->25/tcp, [::]:8131->25/tcp, 0.0.0.0:8132->10027/tcp, [::]:8132->10027/tcp
emailrelay-2.6.1-sink emailrelay:2.6.1 "/usr/local/sbin/ema…" sink 14 seconds ago Up 13 seconds 25/tcp
⚠️ 启动顺序告警可忽略:relay 与 sink 同时拉起时,relay 首次解析
sink可能早于 sink 注册完成,日志出现warning: dns lookup of forward-to address failed: no such host: "sink" (temporary failure in name resolution)。EmailRelay 的转发是连接触发+可重试的,首封信到达时会重新解析,不影响功能。
五、端到端验证(实测回显)
5.1 经内网服务名发信(同网络内客户端视角)
$ swaks --server relay:25 --from alice@example.org --to bob@example.com \
--h-Subject "Compose E2E Test" --body "hello from compose"
=== Trying relay:25...
=== Connected to relay.
<- 220 mail-relay.internal -- E-MailRelay V2.6.1 -- Service ready
-> EHLO ec4b1b171ad8
<- 250-mail-relay.internal says hello
<- 250-VRFY
<- 250-PIPELINING
<- 250 8BITMIME
-> MAIL FROM:<alice@example.org>
<- 250 sender <alice@example.org> OK
-> RCPT TO:<bob@example.com>
<- 250 recipient <bob@example.com> OK
-> DATA
<- 354 start mail input -- end with <CRLF>.<CRLF>
-> (DATA 段发送行,此处略)
<- 250 message processed
-> QUIT
<- 221 OK
=== Connection closed with remote host.
5.2 经宿主映射端口发信(外部客户端视角)
$ swaks --server 10.89.72.1 --port 8131 --from carol@example.org --to dave@example.net \
--h-Subject "Published Port Test" --body "via 8131"
=== Trying 10.89.72.1:8131...
=== Connected to 10.89.72.1.
<- 220 mail-relay.internal -- E-MailRelay V2.6.1 -- Service ready
(EHLO 能力与 DATA 段与 4.1 一致,此处略)
<- 250 message processed
5.3 确认邮件到达 sink 队列
$ docker exec emailrelay-2.6.1-sink ls /var/spool/emailrelay/
emailrelay.1.1792907099.1.content
emailrelay.1.1792907099.1.envelope
emailrelay.1.1792907133.2.content
emailrelay.1.1792907133.2.envelope
5.4 管理接口查状态(宿主 8132)
$ printf 'status\nquit\n' | nc 10.89.72.1 8132
LISTEN: 0.0.0.0:25
LISTEN: 0.0.0.0:10027
LISTEN: ::.10027
LISTEN: ::.25
OUT started: 1
OUT finished: 1
IN started: 2
IN finished: 1
5.5 版本验证
$ docker exec emailrelay-2.6.1-relay /usr/local/sbin/emailrelay --version
E-MailRelay V2.6.1
Copyright (C) 2026 Graeme Walker
(尾部为 OpenSSL 声明与许可行,此处略。)
六、数据持久化说明
| 卷 | 容器内路径 | 内容 |
|---|---|---|
| relay-spool | /var/spool/emailrelay | relay 收信队列(content+envelope 文件对),转发成功即删 |
| sink-spool | /var/spool/emailrelay | sink 落盘的邮件 |
队列即文件系统,备份=拷贝目录;docker compose down 不加 -v 时卷保留,重启后积压邮件不丢。
七、配置参数说明
compose command 数组逐条对应 EmailRelay 命令行选项(镜像 ENTRYPOINT 固定为 /usr/local/sbin/emailrelay,command 即其参数)。本栈用到的参数:
| 参数 | 含义 |
|---|---|
--forward-to sink:10026 | 下游 SMTP 地址(compose 内用服务名解析) |
--forward-on-disconnect | 客户端断开后触发队列转发 |
--port 25 / --admin 10027 | SMTP 监听口 / 管理口 |
--remote-clients | 允许非本机客户端连接(容器间必须) |
--spool-dir | 队列目录(挂卷持久化) |
--no-daemon --user root | 前台运行(容器主进程必须),运行身份 |
--log --log-time --verbose --no-syslog | 日志走 stderr、带时间戳、info 级 |
要加 TLS/AUTH/过滤等更多参数,直接往 command 数组追加即可,完整参数体系见《EmailRelay 2.6.1 配置参数详解》。
八、配置文件挂载(可选)
EmailRelay 选项与配置文件键一一对应。若偏好配置文件而非长 command 数组:
# 宿主编写 emailrelay.conf(键名=去掉前导 -- 的选项名)
forward-to=sink:10026
forward-on-disconnect
port=25
spool-dir=/var/spool/emailrelay
remote-clients
no-daemon
user=root
admin=10027
log
log-time
verbose
no-syslog
compose 挂载并作为位置参数传入:
volumes:
- relay-spool:/var/spool/emailrelay
- ./emailrelay.conf:/usr/local/etc/emailrelay.conf:ro
command: ["/usr/local/etc/emailrelay.conf"]
九、等价 docker run
relay 单服务不编 sink 时,等价命令:
docker build -t emailrelay:2.6.1 .
docker network create --subnet 10.89.72.0/24 emailrelay-net
docker run -d --name emailrelay-2.6.1-relay --hostname mail-relay.internal \
--network emailrelay-net -p 8131:25 -p 8132:10027 \
-v relay-spool:/var/spool/emailrelay \
emailrelay:2.6.1 \
--log --log-time --verbose --no-syslog \
--forward-on-disconnect --forward-to <下游主机>:10026 \
--port 25 --spool-dir /var/spool/emailrelay \
--remote-clients --no-daemon --user root --admin 10027
十、常用运维命令
docker compose logs -f relay # 跟踪转发日志(须按本文展开宏选项才有输出)
docker compose ps # 查看两服务状态与端口映射
docker compose restart relay # 重启转发服务
docker exec emailrelay-2.6.1-relay ls /var/spool/emailrelay # 看 relay 积压
printf 'status\nquit\n' | nc 127.0.0.1 8132 # 管理口状态
printf 'forward\nquit\n' | nc 127.0.0.1 8132 # 手动触发队列转发
docker compose down # 停栈(卷保留);加 -v 连卷一起清
十一、常见问题
Q1:docker logs 一直空白?
用了 --as-proxy/--as-server 宏选项(隐含 --close-stderr),或漏了 --no-syslog。按本文 3.3 的展开写法修改 command 即可。
Q2:relay 日志报 dns lookup of forward-to address failed: no such host: "sink"?
启动竞态的正常现象(见 3.4 说明)。若持续报,检查两服务是否在同一 compose 网络、--forward-to 的主机名拼写。
Q3:外部客户端连 8131 被拒?
确认 --remote-clients 已加(默认只接受本机连接);宿主防火墙放行 8131。
Q4:改 Dockerfile 后镜像没更新?
docker compose build 后需 docker compose up -d 重建容器;仅想换版本可 docker compose build --build-arg EMAILRELAY_VERSION=x.y.z(需把 ARG 传到 wget URL,本文 Dockerfile 已支持改 ARG 值重建)。