Mailcow Docker Compose 部署指南
Mailcow Docker Compose 部署指南:官方 mailcow-dockerized(2026-09a)从 clone、generate_config.sh 配置生成、mailcow.conf 关键变量(端口/降配/子网)到 compose 拉起与验收的完整实测记录,含 SKIP_SOLR 改名、非标端口重定向等真实坑位与回显。
Mailcow Docker Compose 部署指南
目标:在一台 Ubuntu 服务器上以官方 mailcow-dockerized(tag
2026-09a,2026-10-19 实查的最新稳定版)完成 Mailcow 部署:clone → 配置生成 → 降配与端口/子网定制 → 拉起 → 验收。全部命令与回显为 Ubuntu 22.04 + Docker 29.8.1(Compose 插件 v2.x)实测,安装目录/opt/data/mailcow-dockerized/。
一、环境信息
- 操作系统:Ubuntu 22.04(x86_64)
- Docker:29.8.1,含 compose 插件(
docker compose version通过;Mailcow 生成脚本要求 Docker ≥ 24.0,低于此版本会拒绝生成配置) - Mailcow:
2026-09a(GitHub Releases 实查最新稳定 tag) - 安装目录:
/opt/data/mailcow-dockerized/ - 邮件主机名:
mail.example.com,邮件域:example.com(内网环境用/etc/hosts解析,公网环境用 DNS A 记录) - Web 端口:HTTP
8126、HTTPS8130(本机 80/443 与部分高位端口已被同宿主机其他服务占用,故用非标端口,见 4.2) - 部署前内存复核(本机内存吃紧,必须先看):
$ free -h
total used free shared buff/cache available
Mem: 31Gi 23Gi 4.4Gi 333Mi 3.2Gi 6.7Gi
Swap: 2.0Gi 2.0Gi 9.0Mi
Mailcow 官方建议 ≥ 6 GiB 内存,本机 available 约 6.7 GiB,因此按第五章全量关闭 ClamAV/SOGo/全文检索降配部署。
二、获取代码
固定 tag 克隆(走代理时通过环境变量注入):
$ export https_proxy=http://<proxy-host>:<proxy-port> http_proxy=http://<proxy-host>:<proxy-port>
$ git clone --depth 1 -b 2026-09a https://github.com/mailcow/mailcow-dockerized.git /opt/data/mailcow-dockerized
$ cd /opt/data/mailcow-dockerized
$ ls
CODE_OF_CONDUCT.md CONTRIBUTING.md create_cold_standby.sh data
docker-compose.yml generate_config.sh helper-scripts LICENSE
_modules README.md SECURITY.md update.sh
坑位记录:不要用
git clone -b master之后靠脚本切分支——generate_config.sh默认会git checkout -f master把代码切到主线。固定版本 tag 部署时务必用-d(dev mode)运行生成脚本,脚本将跳过分支切换保持当前 tag(本文采用)。
三、生成配置(generate_config.sh 实测)
3.1 前置依赖
$ apt-get install -y jq
2026-09a 的生成脚本强依赖 jq,缺失时直接退出(实测回显):
Error: Cannot find command 'jq'. Cannot proceed.
Solution: Please review system requirements and install requirements. Then, re-run the script.
3.2 运行生成
.env 软链已随仓库提供(指向 mailcow.conf)。以 -d 模式运行并按提示输入主机名:
$ ./generate_config.sh -d
Detecting if your IP is listed on Spamhaus Bad ASN List...
Check completed! Your IP is clean
Press enter to confirm the detected value '[value]' where applicable or enter a custom value.
Enabled Dev Mode.
Not checking out a different branch!
IPv6 not detected on host – disabling IPv6 support.
Skipping Docker IPv6 configuration because host does not support IPv6.
Make sure to check if your docker daemon.json does not include "enable_ipv6": true if you do not want IPv6.
IPv6 configuration complete: ENABLE_IPV6=false
Generating snake-oil certificate...
脚本做了四件事:Spamhaus Bad ASN 检测(无公网 IP 也会快速通过)、主机名/时区交互(时区自动检测为 Asia/Shanghai)、IPv6 探测(本机无 IPv6 默认路由,自动置 ENABLE_IPV6=false 并跳过 Docker IPv6 配置)、生成自签 snake-oil 证书(供无公网证书场景使用)。
3.3 mailcow.conf 关键变量(生成后修改)
$ grep -E '^(MAILCOW_HOSTNAME|HTTP_PORT|HTTPS_PORT|SKIP_CLAMD|SKIP_SOGO|SKIP_FTS|SKIP_LETS_ENCRYPT|IPV4_NETWORK|ENABLE_IPV6|TZ)=' mailcow.conf
MAILCOW_HOSTNAME=mail.example.com
HTTP_PORT=8126
HTTPS_PORT=8130
TZ=Asia/Shanghai
SKIP_LETS_ENCRYPT=y
SKIP_CLAMD=y
SKIP_SOGO=y
SKIP_FTS=y
IPV4_NETWORK=10.89.54
ENABLE_IPV6=false
逐项说明:
| 变量 | 取值 | 理由 |
|---|---|---|
MAILCOW_HOSTNAME | mail.example.com | 邮件服务器 FQDN(非邮件域),证书与 HELO 均用它 |
HTTP_PORT / HTTPS_PORT | 8126 / 8130 | 默认 80/443 已被占用;见 4.2 端口规划 |
HTTP_REDIRECT | n(默认 y) | 见 4.3 非标端口重定向坑位 |
SKIP_LETS_ENCRYPT | y | 无公网域名,ACME 无法验证,关闭避免空转 |
SKIP_CLAMD / SKIP_SOGO / SKIP_FTS | y / y / y | 内存紧张全量降配,见第五章 |
IPV4_NETWORK | 10.89.54 | 自定义内部网络网段(生成 10.89.54.0/24),避免与默认 172.22.1.0/24 及宿主其他网络冲突 |
ENABLE_IPV6 | false | 脚本自动探测写入 |
坑位记录(版本改名):老教程里的
SKIP_SOLR=y在 2026-09a 已改为SKIP_FTS=y(全文检索后端由 Solr 换成 Dovecot 内置 Flatcurve/Xapian),写错变量不会报错,只会让降配不生效。
3.4 数据库与随机密钥
生成的 mailcow.conf 内含自动生成的 DBPASS、DBROOT、REDISPASS(28 位随机串),部署后请连同整个文件妥善备份(权限已是 600)。
四、端口与网络规划
4.1 邮件协议端口(宿主直绑)
25/110/143/465/587/993/995/4190 全部空闲,保持默认直绑;内部端口(SQL 13306、Redis 7654、Doveadm 19991)默认仅绑 127.0.0.1。部署后实测监听:
$ ss -ltn | grep -E ':(8126|8130|25|110|143|465|587|993|995|4190)\s'
LISTEN 0 4096 0.0.0.0:8126 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:8130 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:25 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:110 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:143 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:4190 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:465 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:587 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:993 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:995 0.0.0.0:*
4.2 Web 端口选择
本机 80/443 被占用,且 8125、8127–8129 等高位端口亦被同宿主机其他服务监听(部署前 ss -ltn 复核为部署纪律)。故 Web 入口选 HTTP_PORT=8126、HTTPS_PORT=8130(两者实测空闲)。若你的 8127 空闲,用 HTTPS_PORT=8127 亦完全等价——这只是两个普通变量。
4.3 坑位记录:非标端口下 HTTP 301 丢端口
HTTP_REDIRECT=y(默认)时,访问 http://mail.example.com:8126/ 会被 301 到 https://mail.example.com/(丢失 8130,落在 443 上)。官方模板按标准端口假设生成重定向。两种处理:
- 保持
HTTP_REDIRECT=n:8126 直接吐 UI(本地/内网场景实测选择); - 或改用标准 80/443 绑定。
本机改法与验证:
$ sed -i 's/^HTTP_REDIRECT=y$/HTTP_REDIRECT=n/' mailcow.conf
$ docker compose up -d
Container mailcowdockerized-nginx-mailcow-1 Started
$ curl -s -o /dev/null -w '%{http_code}\n' http://mail.example.com:8126/
200
4.4 内部网络子网
Mailcow 内部网络由 IPV4_NETWORK 控制(compose 网络段定义为 ${IPV4_NETWORK:-172.22.1}.0/24)。设为 10.89.54 后实测:
$ docker network inspect mailcowdockerized_mailcow-network --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
10.89.54.0/24
网桥名 br-mailcow(compose driver_opts 固定),宿主路由不受影响。
五、降配部署(低内存场景)
本机可用内存约 6.7 GiB,mailcow.conf 三开关全开:
SKIP_CLAMD=y # 关闭 ClamAV 病毒扫描
SKIP_SOGO=y # 关闭 SOGo(Webmail/CalDAV/CardDAV/ActiveSync)
SKIP_FTS=y # 关闭 Dovecot 全文检索(Flatcurve)
拉起后验证降配的真实形态——被跳过的服务容器并不消失,而是只跑保活进程(实测容器内进程为 /bin/bash /clamd.sh + sleep 365d),日志明确打印跳过原因:
$ docker logs mailcowdockerized-clamd-mailcow-1
SKIP_CLAMD=y, skipping ClamAV...
$ docker logs mailcowdockerized-sogo-mailcow-1
SKIP_SOGO=y, skipping SOGo...
Rspamd 会自动探测 ClamAV 缺位并降级,实测外域入站信的 X-Spamd-Result 出现:
CLAM_VIRUS_FAIL(0.00)[failed to scan and retransmits exceed];
即病毒检查记 0 分放行、不阻塞投递。恢复某组件:把对应变量改回 n 后 docker compose up -d 重建即可。
六、拉起与验收
6.1 启动
$ docker compose up -d
首次会拉取 15 个官方构建镜像(ghcr.io/mailcow/*,总下载量约 1.5 GB),随后按依赖顺序启动。
6.2 实测最终状态
$ docker compose ps --format '{{.Name}}\t{{.Status}}'
mailcowdockerized-acme-mailcow-1 Up ...
mailcowdockerized-clamd-mailcow-1 Up ... (healthy) # 降配保活
mailcowdockerized-dockerapi-mailcow-1 Up ...
mailcowdockerized-dovecot-mailcow-1 Up ...
mailcowdockerized-memcached-mailcow-1 Up ...
mailcowdockerized-mysql-mailcow-1 Up ...
mailcowdockerized-netfilter-mailcow-1 Up ...
mailcowdockerized-nginx-mailcow-1 Up ...
mailcowdockerized-ofelia-mailcow-1 Up ...
mailcowdockerized-olefy-mailcow-1 Up ...
mailcowdockerized-php-fpm-mailcow-1 Up ...
mailcowdockerized-postfix-mailcow-1 Up ...
mailcowdockerized-postfix-tlspol-mailcow-1 Up ...
mailcowdockerized-redis-mailcow-1 Up ...
mailcowdockerized-rspamd-mailcow-1 Up ...
mailcowdockerized-sogo-mailcow-1 Up ... # 降配保活
mailcowdockerized-unbound-mailcow-1 Up ... (healthy)
mailcowdockerized-watchdog-mailcow-1 Up ...
6.3 Web 入口验收
$ curl -s -o /dev/null -w '%{http_code}\n' http://mail.example.com:8126/
200
$ curl -s http://mail.example.com:8126/ | grep -oE '<title>[^<]+</title>'
<title>mail.example.com - mail UI</title>
首次登录用户名 admin、密码 moohoo(生成脚本注释原文:# Default password is "moohoo"),登录后立即改强密码——流程见快速入门篇。
6.4 主机名解析准备
内网无 DNS 时把主机名与自动发现名指到本机(公网环境应为 A 记录):
$ echo '127.0.0.1 mail.example.com autodiscover.example.com autoconfig.example.com' >> /etc/hosts
$ getent hosts mail.example.com
127.0.0.1 mail.example.com
七、常见问题(部署期实测)
jq缺失:按 3.1 安装后重跑。- 固定 tag 被切分支:用
./generate_config.sh -d,不要交互选 master/nightly。 SKIP_SOLR无效:2026-09a 已改名SKIP_FTS(3.3 节)。- HTTP 301 丢端口:非标端口下设
HTTP_REDIRECT=n(4.3 节)。 - 修改 mailcow.conf 后未生效:compose 不会自动重建已存在容器,需
docker compose up -d(必要时对目标服务--force-recreate,如开启 API 后重建php-fpm-mailcow/nginx-mailcow)。 - 降配后 ClamAV 报
CLAM_VIRUS_FAIL:属预期降级行为(第五章),非故障。 fatal: HEAD does not point to a branch:-d模式下 detached HEAD 查询 upstream 的无害提示,可忽略。
八、卸载与清理
$ cd /opt/data/mailcow-dockerized
$ docker compose down -v # 移除容器、网络与命名卷(邮件数据全清,慎重)
$ rm -rf /opt/data/mailcow-dockerized
仅停机保留数据用 docker compose down(不带 -v);升级/迁移请走 update.sh 或冷备脚本(见运维手册篇)。