VPS 是 **Ubuntu 22.04**,命令行部署 **docker-mailserver**(DMS)的流程。整个过程基于官方最新推荐方式(2025–2026 版本,使用 `compose.yaml` + `setup` 脚本)。 ### 前置准备(必须先完成) 1. 更新系统 & 安装基本工具 ```bash sudo apt update && sudo apt upgrade -y sudo apt install -y curl git nano ufw apparmor # apparmor 推荐保留,DMS 用到 ``` 2. 安装 Docker + Docker Compose(Ubuntu 22.04 默认仓库的版本较旧,推荐官方源安装最新版) ```bash # 卸载旧版(如果有) for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done # 安装官方源 sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 验证 docker --version docker compose version # 应该看到 v2.xx(如 v2.24+) ``` 3. 把当前用户加到 docker 组(避免每次 sudo docker) ```bash sudo usermod -aG docker $USER # 然后退出 SSH 重新登录生效 ``` 4. 防火墙(ufw)放行必要端口(先别开 25,如果云厂商封了 25 端口后续再说) ```bash sudo ufw allow 22 sudo ufw allow 80 # 如果要用 certbot 先开 sudo ufw allow 443 sudo ufw allow 587 sudo ufw allow 465 sudo ufw allow 993 # IMAP SSL sudo ufw enable sudo ufw status ``` ### 正式部署步骤 1. 创建工作目录 ```bash mkdir -p ~/mailserver && cd ~/mailserver mkdir -p docker-data/dms/{mail-data,mail-state,mail-logs,config} ``` 2. 下载 compose.yaml(官方模板) ```bash curl -o compose.yaml \ https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/compose.yaml ``` (或者你也可以直接用 nano 编辑创建一个,内容参考下面最小化示例) 3. 编辑 compose.yaml(nano compose.yaml) 至少修改这些关键地方(替换成你自己的): ```yaml services: mailserver: image: ghcr.io/docker-mailserver/docker-mailserver:latest container_name: mailserver hostname: mail.yourdomain.com # ← 你的完整域名(FQDN) ports: - "25:25" - "465:465" - "587:587" - "993:993" volumes: - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment: - ENABLE_FAIL2BAN=1 - SSL_TYPE=letsencrypt # 先用 'self-signed' 测试,后面换 - PERMIT_DOCKER=host # 或 'connected-networks' 根据需要 - SPOOF_PROTECTION=1 # 推荐开启,防伪造 cap_add: - NET_ADMIN # Fail2Ban 需要 restart: always ``` 保存退出。 4. 首次启动 & 拉镜像 ```bash docker compose pull docker compose up -d ``` 查看日志(等 1-2 分钟初始化完): ```bash docker logs -f mailserver ``` 5. 下载 setup 脚本(管理用户、DKIM 等神器) ```bash curl -o setup https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh chmod +x setup ``` 6. 生成 DKIM 密钥(强烈推荐,否则投递率惨) ```bash ./setup config dkim ``` 生成后查看: ```bash cat docker-data/dms/config/opendkim/keys/yourdomain.com/*.txt ``` 把里面的 TXT 记录加到 DNS(格式如:`selector._domainkey` → `v=DKIM1; k=rsa; p=...`) 7. 添加邮箱账号 ```bash ./setup email add user@yourdomain.com 'YourVeryStrongPassword123!' ./setup email list ``` 8. (可选)测试发送邮件 - 用邮件客户端(Thunderbird/Outlook)配置: - IMAP: mail.yourdomain.com 993 SSL/TLS - SMTP: mail.yourdomain.com 587 STARTTLS 或 465 SSL/TLS - 发一封到 https://www.mail-tester.com/ 测试得分(目标 9/10+) ### 常见 Ubuntu 22.04 注意事项 - **端口 25 被封**:很多 VPS(如 Vultr、Linode、DigitalOcean)默认封 25 出站。用 `./setup config relay` 配置外部中继(如 Amazon SES、Mailgun、SendGrid)。 - **Let's Encrypt 证书**:推荐后面加 certbot 或 acme.sh 获取证书,然后改 `SSL_TYPE=letsencrypt` 并挂载 `/etc/letsencrypt` 卷。 - **内存**:最低 2GB 建议,4GB+ 更稳(有 Fail2Ban + ClamAV 时)。 - **更新**:以后升级只需: ```bash docker compose pull && docker compose up -d ``` ### 第二阶段 你运行 `./setup email add test@mail.newddf.xyz '12345'` 报错 `-bash: ./setup: No such file or directory`,说明当前目录下**根本没有 setup 文件**(或它叫 setup.sh,但你没下载/没放对位置)。 从你的日志看,容器是 v15.1.0(最新版),docker-mailserver 的 setup 工具有两种主流用法: - **推荐方式(官方最新建议)**:不用下载 setup.sh 脚本,直接用 `docker exec` 调用容器里的内置 `setup` 命令(最干净,不用额外文件)。 - **备选方式**:下载 setup.sh 包装脚本(旧习惯,但现在不强制)。 ### 首选解决:直接用 docker exec(不用下载任何 setup 文件) 1. 先确认容器在运行: ```bash docker ps # 看到 mailserver 容器在跑就行 ``` 2. 用下面命令**直接添加邮箱账号**(替换你的域名和密码): ```bash docker exec -ti mailserver setup email add test@mail.newddf.xyz '12345' ``` - `-ti`:交互模式(如果要输入密码更安全) - `mailserver`:你的容器名(从 compose.yaml 里来的,如果改过就用你自己的 container_name) - 密码用单引号包住,避免特殊字符问题(你的 '12345' 没问题,但以后强密码建议复杂点) 3. 添加成功后,检查列表: ```bash docker exec -ti mailserver setup email list ``` 4. 如果添加后 Dovecot 还没起来(看 logs 还在倒计时),等几秒或重启容器: ```bash docker compose restart ``` 这次因为有账号了,就不会再 shutdown 了。 ### 如果你还是想用 ./setup 方式(下载脚本) 1. 在 ~/mailserver 目录下下载官方 setup.sh: ```bash wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh -O setup.sh chmod +x setup.sh ``` (或用 curl: ```bash curl -o setup.sh https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh chmod +x setup.sh ``` ) 2. 然后运行: ```bash ./setup.sh email add test@mail.newddf.xyz '12345' ``` 这个脚本会自动检测容器并 exec 进去执行(如果容器没跑,它甚至会临时拉一个容器帮你跑命令)。 ### 为什么之前没文件? 你可能漏掉了下载 setup 的步骤(我之前回复里有 `curl -o setup ...` 或类似,但你没执行)。现在用 docker exec 方式最省事,不用管文件。 ### 验证成功后下一步 - 检查 logs: ```bash docker logs -f mailserver ``` 看到 Dovecot 启动成功(无警告)。 - 生成 DKIM: ```bash docker exec -ti mailserver setup config dkim ``` 或用 ./setup.sh 如果你下载了。 - DNS 加 TXT 记录(从生成的 .txt 文件复制)。 - 测试发信:用邮件客户端连 587/465 端口发一封到 mail-tester.com 测得分。 ###第三阶段 你遇到的错误 `-bash: demoCA/cacert.pem: No such file or directory` 是因为你直接把 `cat mail.newddf.xyz-cert.pem > demoCA/cacert.pem` 当成命令敲了,但 `demoCA` 目录还没创建,所以 cat 找不到目标路径。 ### 正确一步步执行(在宿主机 ~/mailserver 目录下) 1. **创建目录结构**(必须先建 demoCA) ```bash cd ~/mailserver/docker-data/dms/ssl mkdir -p demoCA ``` 2. **生成自签名证书**(如果还没生成,重新跑一次) ```bash openssl req -new -nodes -x509 -subj "/C=US/ST=New York/L=Buffalo/O=SelfSigned/CN=mail.newddf.xyz" -days 3650 -keyout mail.newddf.xyz-key.pem -out mail.newddf.xyz-cert.pem ``` - 这会生成两个文件:`mail.newddf.xyz-key.pem` 和 `mail.newddf.xyz-cert.pem` 3. **创建 CA 文件(self-signed 模式下需要这个文件,即使是假的)** ```bash cat mail.newddf.xyz-cert.pem > demoCA/cacert.pem echo "01" > demoCA/serial touch demoCA/index.txt ``` - 现在目录结构应该是: ``` docker-data/dms/ssl/ ├── mail.newddf.xyz-cert.pem ├── mail.newddf.xyz-key.pem └── demoCA/ ├── cacert.pem ├── index.txt └── serial ``` 4. **确认文件存在**(检查权限和内容) ```bash ls -l ~/mailserver/docker-data/dms/ssl/ ls -l ~/mailserver/docker-data/dms/ssl/demoCA/ ``` 看到文件就 OK。 5. **重启容器**(让 DMS 使用这些证书) ```bash cd ~/mailserver docker compose down docker compose up -d ``` 6. **检查日志是否通过证书检查** ```bash docker logs mailserver | tail -n 50 ``` - 不再看到 "File ... does not exist" 或 "Shutting down" - 应该看到类似 "Using provided self-signed certificate" 或 "TLS setup complete" - 如果成功,Dovecot/Postfix 会启动,你可以再试: ```bash docker exec -ti mailserver setup email list ``` 现在应该正常列出 `test@mail.newddf.xyz` 了。 ### 如果还是报证书不存在 - 确认 compose.yaml 的 volumes 有这一行(挂载你手动生成的 ssl 目录): ```yaml volumes: ... - ./docker-data/dms/ssl/:/tmp/docker-mailserver/ssl/ ``` - 如果你不想手动生成证书,改成最简单方式: 在 compose.yaml environment 加: ```yaml - SSL_TYPE=manual - DISABLE_TLS=true ``` 然后 down && up -d,先让容器跑起来(无 TLS),测试收发邮件后再修复证书。 ###第四阶段 compose.yaml 里面有几处小问题,导致挂载失败和启动卡住。下面我一步步帮你修复。 ### 主要问题分析 1. **volumes 部分有重复和注释错误**: - 你写了两次 `./docker-data/dms/ssl/`,而且第二次是注释,但格式不对,导致 Docker 解析失败。 - 注释行不能写成 `- ./docker-data/dms/ssl/ #是宿主机路径...`,Docker 会把注释当成路径的一部分,报错 "invalid mount path"。 2. **正确挂载只需写一次**: - 宿主机路径:`./docker-data/dms/ssl/`(相对 compose.yaml 位置) - 容器路径:`/tmp/docker-mailserver/ssl/` 3. **其他小建议**: - `ports` 里加了 143(明文 IMAP),如果想强制 TLS 可以去掉或注释。 - `healthcheck` 是默认的,保留就好。 ### 修复后的完整 compose.yaml(复制粘贴替换你的文件) ```yaml services: mailserver: image: ghcr.io/docker-mailserver/docker-mailserver:latest container_name: mailserver hostname: mail.newddf.xyz ports: - "25:25" - "465:465" - "587:587" - "993:993" # - "143:143" # 明文 IMAP,测试时可选开启,生产建议关闭 volumes: - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro # 关键:挂载手动生成的证书目录(只需这一行) - ./docker-data/dms/ssl/:/tmp/docker-mailserver/ssl/ environment: - ENABLE_FAIL2BAN=1 - SSL_TYPE=self-signed - PERMIT_DOCKER=host - SPOOF_PROTECTION=1 # 如果内存紧张,可以加这些关闭重功能 # - ENABLE_CLAMAV=0 # - ENABLE_SPAMASSASSIN=0 cap_add: - NET_ADMIN # Fail2Ban 需要 restart: always stop_grace_period: 1m healthcheck: test: "ss --listening --ipv4 --tcp | grep --silent ':smtp' || exit 1" timeout: 3s retries: 0 ``` ### 操作步骤 1. **替换 compose.yaml** 用 nano 编辑文件,把上面内容完整复制进去覆盖原有内容(尤其是 volumes 部分)。 ```bash nano compose.yaml ``` 保存退出。 2. **清理并重新启动** ```bash docker compose down docker compose up -d ``` 3. **检查状态和日志** ```bash docker compose ps # 看容器是否 running 和 healthy docker logs mailserver | tail -n 100 # 看最后100行日志 ``` - 如果成功,日志里**不会再出现** "does not exist" 或 "Shutting down"。 - 应该看到证书被加载的信息(如 "Using provided self-signed certificate"),然后 Dovecot/Postfix 启动成功。 4. **验证账号列表** ```bash docker exec -ti mailserver setup email list ``` 现在应该正常显示 `test@mail.newddf.xyz` 而不再报 socket 错误。 ### 如果还是失败 - 把 `docker logs mailserver | tail -n 50` 完整贴出来。 - 运行下面命令确认挂载生效: ```bash docker exec -ti mailserver ls -l /tmp/docker-mailserver/ssl/ ``` 如果看到你的证书文件(key、cert、demoCA),说明挂载对了。 修复完路径后,这次容器就能稳定启动,使用你手动生成的证书,Dovecot 也会起来。成功后我们可以继续配置 DKIM 和测试发信。 ###第五阶段 是的,你需要在 Cloudflare 上为 `mail.newddf.xyz` 和主域名 `newddf.xyz` 做一些 DNS 操作。这些操作是自建邮箱正常收发邮件、提升投递率(不进垃圾箱)的**必须步骤**。 以下是完整清单,按优先级排序: ### 1. 最基本(必须做,否则收不到邮件) - **A 记录**(指向你的 VPS IP) - 类型:A - 名称:mail - 内容:你的 VPS 公网 IPv4 地址(比如 123.45.67.89) - Proxy status:**DNS only**(灰色云朵,不要橙色代理!因为邮件服务器端口 25/465/587 不能走 Cloudflare 代理) - **MX 记录**(告诉别人你的邮件服务器在哪里) - 类型:MX - 名称:@ (或留空,代表 newddf.xyz) - 内容:mail.newddf.xyz - 优先级:10(默认即可) - Proxy status:DNS only ### 2. 强烈推荐(不做投递率很低,容易进垃圾箱) - **SPF 记录**(防伪发信,告诉别人只有你的服务器能发 @newddf.xyz 的邮件) - 类型:TXT - 名称:@ (或留空) - 内容:`v=spf1 mx a:mail.newddf.xyz ~all` - 解释:允许 MX 记录指向的服务器 + mail.newddf.xyz 的 IP 发信,~all 表示软失败(推荐先用这个,后面可改 -all 严格拒绝) - **DKIM 记录**(签名验证,防篡改) - 类型:TXT - 名称:mail._domainkey (selector 是 mail,如果你改了 selector 就改成 xxx._domainkey) - 内容:从 `mail.txt` 复制的完整字符串,例如: ``` v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...(去掉引号,合并成一行) ``` - Proxy status:DNS only - **DMARC 记录**(报告伪造邮件,结合 SPF/DKIM 使用) - 类型:TXT - 名称:_dmarc - 内容:先用宽松模式测试: ``` v=DMARC1; p=none; rua=mailto:test@mail.newddf.xyz; ruf=mailto:test@mail.newddf.xyz; fo=1 ``` - p=none:只报告,不阻挡 - rua/ruf:报告地址(可以改成你的邮箱) - fo=1:失败时报告 - 等几天看报告正常后,可改 p=quarantine 或 p=reject ### 3. 可选但推荐 - **PTR / rDNS**(反向解析):联系 RackNerd 支持,让他们把你的 VPS IP 反向解析设置为 `mail.newddf.xyz`。很多大厂(如 Gmail、QQ)会检查这个,不匹配容易拒收。 - **CAA 记录**(可选,限制证书颁发机构) - 类型:CAA - 名称:@ - 内容:0 issue "letsencrypt.org" (如果你用 Let's Encrypt) ### 操作注意事项(Cloudflare 后台) 1. 所有记录的 **Proxy status 必须是 DNS only**(灰云),不能是 Proxied(橙云),否则邮件无法正常传输。 2. TTL 建议设为 300~3600 秒,别用 Auto。 3. 加完后,用下面命令验证: ```bash dig A mail.newddf.xyz dig MX newddf.xyz dig TXT newddf.xyz # SPF dig TXT mail._domainkey.newddf.xyz # DKIM dig TXT _dmarc.newddf.xyz ``` 看到正确答案就 OK。 ### 总结优先级表 | 优先级 | 记录类型 | 名称 | 内容示例 | 是否必须 | |--------|----------|--------------------|-----------------------------------------------|----------| | 1 | A | mail | 你的 VPS IP | 必须 | | 2 | MX | @ | mail.newddf.xyz (优先级 10) | 必须 | | 3 | TXT | @ | v=spf1 mx a:mail.newddf.xyz ~all | 强烈推荐 | | 4 | TXT | mail._domainkey | v=DKIM1; k=rsa; p=...(从 mail.txt 复制) | 强烈推荐 | | 5 | TXT | _dmarc | v=DMARC1; p=none; rua=... | 推荐 | 做完这些,你的邮箱投递率会大幅提升(尤其是发到 Gmail/Outlook/QQ 等)。 如果你现在加了 DKIM TXT,请贴 `cat docker-data/dms/config/opendkim/keys/newddf.xyz/mail.txt` 的内容,我帮你检查格式是否正确,以及怎么粘贴到 Cloudflare。 或者贴 dig 输出,我帮你验证是否传播成功。继续加油! ###第六阶段 你的日志输出显示: - Dovecot 启动正常(master starting up for imap, lmtp) - Postfix 启动正常(daemon started) - 有一次 IMAP 登录成功(来自 40.90.178.13,这是 Microsoft Outlook/Exchange 的 IP,很可能是 Mac Mail 客户端连上了) - amavis 报了一个无害警告(No ext program for .doc),不影响收发 **但关键问题**:日志里**没有任何外部投递记录**(没有 postfix/smtpd 收到邮件、没有 lmtp 递交、没有 delivered/sent/bounce/reject/timeout 等关键词)。 这说明**外部邮件根本没到达你的服务器**(或被 Postfix 早期拒绝了,没进日志尾部)。结合你之前 ss 输出只看到 993(客户端收信端口),但没看到 25(SMTP 接收端口)或 4190(LMTP),问题基本锁定在**入站端口 25 被防火墙或 VPS 提供商封锁**。 ### 当前诊断总结 | 现象 | 可能原因 | 确认方式 | 概率 | |------|----------|----------|------| | 能发邮件 | Postfix 出站 25/465/587 正常 | 已确认 | 高 | | 收不到邮件 | 外部 MTA 连不上你的 25 端口 | 日志无投递记录 | 95% | | 993 监听正常 | 客户端能连 IMAP 查看旧邮件 | 已确认登录记录 | - | | 25/4190 未见监听 | ports 映射缺失或防火墙挡 | ss 只显示 993 | 高 | ### 立即修复步骤 1. **加 25 和 4190 端口映射(你 compose.yaml 缺少 25 的显式映射,虽然默认有,但加显式更保险)** 编辑 compose.yaml: ```bash nano compose.yaml ``` ports 部分改成: ```yaml ports: - "25:25" # 必须显式加,确保外部投递 - "465:465" - "587:587" - "993:993" - "4190:4190" # LMTP 投递关键 # - "143:143" # 可选 ``` 保存,重启: ```bash docker compose down docker compose up -d ``` 2. **确认所有关键端口监听** ```bash sudo ss -tulnp | grep -E ':25|:465|:587|:993|:4190' ``` - 应该看到 5 个 docker-proxy 监听。 - 如果 25/4190 还是没有 → compose.yaml 没保存成功,或 docker compose 命令没生效。 3. **开放防火墙** ```bash sudo ufw allow 25/tcp sudo ufw allow 4190/tcp sudo ufw reload sudo ufw status | grep -E '25|4190|993' ``` 4. **测试外部能否连上你的 25 端口** 从**你的本地电脑或另一台机器**(不是 VPS 本身)跑: ```bash telnet mail.newddf.xyz 25 ``` - 如果连上看到 `220 mail.newddf.xyz ESMTP Postfix` → 25 端口通了。 - 如果 connection refused / timeout → 端口被封: - **本地防火墙**挡了(ufw allow 25)。 - **RackNerd VPS 防火墙**挡了(最常见):登录 RackNerd 面板 → VPS → Firewall → 加入站规则 TCP 25 (来源 Any)。 - 如果 RackNerd 默认封 25(很多廉价 VPS 都封),联系 support 申请开放:“我需要开放 25 端口用于自建邮件服务器接收邮件”。 5. **再发测试邮件并看日志** 从 Gmail 发一封到 test@mail.newddf.xyz。 立即跑: ```bash docker logs mailserver | tail -n 300 ``` 贴给我完整尾部(不要 grep),看是否有 postfix/smtpd 收到连接。 ### 额外建议 - 如果 25 端口最终确认被 RackNerd 封(telnet 失败),有两种方案: 1. 申请解封(成功率高,说明用途是自建邮箱)。 2. 用外部中继收信(临时方案):用 Gmail/QQ 邮箱转发到你的服务器(但麻烦),或用第三方 SMTP 中继(如 Amazon SES、SendGrid 免费层)。 先加 4190 和 25 映射 → 重启容器 → 跑 ss 确认端口 → 发测试邮件 → 贴日志尾部。