Docker+Roundcube+Resend+QQ邮箱:自建域名邮箱系统实战
1. 为什么我要折腾这套自建邮箱方案
先说说我做这件事的起因。手上管着好几个域名,平时注册各种服务、接收验证邮件、跟客户沟通,全都依赖邮箱。用公共邮箱吧,隐私是个问题,而且域名邮箱看起来更专业;用大厂的企业邮箱吧,免费额度卡得死,稍微上点规模就要收费,一年下来也是笔不小的开销。于是我就琢磨着,能不能自己搭一套,既能用自己的域名收发信,又能有个像样的网页界面,最好还能省掉那些繁琐的授权登录步骤。
这套方案的核心就是三个东西:Docker 负责把服务跑起来,Roundcube 提供网页版邮箱界面,Resend 负责把信发出去,QQ 邮箱 负责把信收进来。听起来有点绕,我拆开讲你就明白了。发信这块,自建邮件服务器最大的坑就是 IP 信誉和反向解析,你辛辛苦苦搭个 Postfix,结果发出去的邮件全进对方垃圾箱,白忙活。Resend 这类服务就是帮你解决发信通道的问题,你只管调它的接口,投递和信誉它来扛。收信这块,我直接用 QQ 邮箱的域名邮箱功能,把发到我域名的邮件转发到 QQ 邮箱,然后 Roundcube 通过 IMAP 去拉取。这样一来,收信走 QQ 的稳定通道,发信走 Resend 的专业通道,两边都不用自己维护复杂的邮件服务器。
那 Roundcube 在这里扮演什么角色?它就是个网页前端,让你不用装 Outlook、Foxmail 这些客户端,打开浏览器就能收发邮件、管理文件夹、看通讯录。对于我这种经常换设备、又不想每台机器都配一遍客户端的人来说,网页版是真香。而且 Roundcube 支持插件,可以做一些自动化的事情,比如自动登录——这就是标题里说的“告别授权”。传统的邮箱客户端或者网页登录,每次都要输密码,或者走 OAuth 授权跳转,麻烦得很。我这套方案里,Roundcube 配置了自动登录插件,配合 Docker 内部网络,打开页面直接进邮箱,省掉登录这一步。
适合谁来参考?如果你手上有域名,想搞个自己的邮箱系统,又不想被复杂的邮件服务器配置劝退,那这套方案很适合你。如果你只是偶尔收收验证码,那没必要折腾,直接用 QQ 邮箱就行。但如果你像我一样,需要域名邮箱撑门面,又想要网页版的便利,还希望发信稳定不进垃圾箱,那接着往下看。
提示:这套方案涉及域名解析、Docker 基础操作和第三方服务配置,建议你对 Linux 命令行和 Docker 有基本了解。完全零基础的话,可以先补一下 Docker 的安装和常用命令,不然中间卡住了会比较难受。
2. 整体架构与核心组件选型思路
2.1 为什么是 Roundcube 而不是其他网页邮箱
网页邮箱客户端的选择其实不少,比如 RainLoop、AfterLogic、SOGo 这些。我选 Roundcube 有几个很实际的原因。第一,它够老牌,社区活跃,遇到问题搜一下基本都有答案,不会卡在某个冷门 bug 上没人理。第二,它的插件生态成熟,我需要的自动登录、身份管理、附件预览这些功能都有现成插件,不用自己从头写。第三,它的界面虽然不算惊艳,但胜在稳定,不会今天更新明天崩,对于我这种只想安安静静收信的人来说,稳定比花哨重要得多。
RainLoop 我也试过,界面确实更现代,但它的开发节奏慢下来了,有些新特性跟不上,而且插件生态不如 Roundcube 丰富。SOGo 更偏向企业协同,功能太重,部署起来也复杂,我一个人用不着那么多东西。所以综合下来,Roundcube 是最平衡的选择。
2.2 Resend 在发信环节的角色
前面说了,自建邮件服务器发信最大的难题是 IP 信誉。你从自己的 VPS 发出去的邮件,大概率会被 Gmail、Outlook 这些大厂标记为垃圾邮件,因为你的 IP 没有发信历史,也没有做反向解析和 SPF、DKIM、DMARC 这些认证。Resend 这类服务就是帮你解决这个问题的。你注册一个账号,验证你的域名,它会给你一套 API 密钥和 SMTP 配置,你通过它来发信,投递成功率比你自建高得多。
那为什么不用 QQ 邮箱的 SMTP 发信?因为 QQ 邮箱的 SMTP 发信有频率限制,而且发出去的邮件会显示“由 QQ 邮箱代发”,看起来不够专业。Resend 可以让你用自己的域名发信,收件人看到的是 you@yourdomain.com,而不是 yourname@qq.com 代发,体验好很多。而且 Resend 有免费额度,每个月几千封,个人用完全够了。
2.3 QQ 邮箱收信的巧妙之处
收信这块,我选择让 QQ 邮箱来扛。具体做法是,在 QQ 邮箱里开启“域名邮箱”功能,把你的域名绑定上去,然后设置转发规则,把所有发到 you@yourdomain.com 的邮件转发到你的 QQ 邮箱。这样邮件先到 QQ 的服务器,再由 Roundcube 通过 IMAP 拉取。
为什么这么绕?因为直接让 Roundcube 收信,你得自己搭 MX 记录、自己维护邮件服务器,还要处理垃圾邮件过滤、病毒扫描这些破事。QQ 邮箱的收信通道已经非常成熟了,垃圾邮件识别率很高,而且稳定,几乎不会丢信。你只需要把 MX 记录指向 QQ 邮箱的服务器,剩下的它帮你搞定。Roundcube 这边只需要配置 IMAP 连接,去 QQ 邮箱拉信就行,简单又可靠。
2.4 Docker 编排的整体布局
整套服务我用 Docker Compose 来编排,主要包含这几个容器:Roundcube 本体、MariaDB(存 Roundcube 的配置和用户数据)、以及一个可选的 Nginx 做反向代理。如果你想让 Roundcube 通过 HTTPS 访问,Nginx 这块不能省。数据库用 MariaDB 而不是 MySQL,是因为 MariaDB 更轻量,资源占用小,对于个人邮箱这种低并发场景完全够用。
网络方面,我建了一个独立的 Docker 网络,让 Roundcube 和 MariaDB 在内部互通,不暴露数据库端口到公网。Roundcube 的 Web 端口通过 Nginx 反代出去,或者直接映射到宿主机。如果你有 Cloudflare,还可以套一层 CDN,顺便把 HTTPS 证书也省了。Cloudflare 的免费套餐支持 SSL,配置也简单,对于个人项目来说很划算。
注意:数据库容器千万不要把 3306 端口映射到公网,Roundcube 和数据库在同一个 Docker 网络里,用容器名就能互通,没必要暴露出去。我见过有人图省事直接把数据库端口开到公网,结果被扫到弱密码,数据全没了。
3. 核心细节解析与实操要点
3.1 域名解析的关键记录配置
域名解析是整套方案的地基,配错了后面全白搭。你需要配置这几条记录:
| 记录类型 | 主机记录 | 记录值 | 用途 |
|---|---|---|---|
| MX | @ | 你 QQ 域名邮箱提供的 MX 地址 | 收信 |
| TXT | @ | Resend 提供的 SPF 记录 | 发信认证 |
| TXT | resend._domainkey | Resend 提供的 DKIM 记录 | 发信签名 |
| TXT | _dmarc | 自定义 DMARC 策略 | 发信策略 |
| A | 你的服务器 IP | Roundcube 访问 |
MX 记录指向 QQ 邮箱的服务器,这样别人发给你的邮件会先到 QQ。SPF 和 DKIM 是 Resend 要求的,你验证域名的时候它会给你具体的值,照着填就行。DMARC 记录我建议先设成 v=DMARC1; p=none; rua=mailto:you@yourdomain.com,先观察一段时间,确认发信没问题再收紧策略。直接上 p=reject 容易误伤,我就吃过这个亏,调了半天才发现是自己的 DMARC 太严了。
A 记录指向你的服务器 IP,这样你可以通过 mail.yourdomain.com 访问 Roundcube。如果你用 Cloudflare 代理,记得把这条记录设成“仅 DNS”模式,不然 Cloudflare 的代理会干扰邮件相关的流量。
3.2 Resend 域名验证与 SMTP 配置
Resend 的域名验证流程不复杂,但有几个细节容易踩坑。注册账号后,在 Domains 页面添加你的域名,它会给你一组 DNS 记录,包括 SPF、DKIM 和 MX(用于接收退信)。你把这些记录加到域名解析里,然后点验证。验证通过后,在 API Keys 页面生成一个密钥,这个密钥就是你在 Roundcube 里配置 SMTP 时要用的密码。
Roundcube 的 SMTP 配置里,服务器填 smtp.resend.com,端口用 465(SSL)或者 587(STARTTLS),用户名填 resend,密码填你生成的 API 密钥。这里有个坑:Resend 的 SMTP 用户名固定是 resend,不是你的邮箱地址,我第一次配的时候填了自己的邮箱,结果一直认证失败,折腾了好久才反应过来。
提示:Resend 免费额度是每月 3000 封,每天 100 封。个人用完全够,但如果你要群发邮件,得注意别超了。超了之后发信会被拒,得等第二天恢复。
3.3 Roundcube 自动登录插件的原理与配置
自动登录是这套方案里最提升体验的部分。传统登录方式,你打开 Roundcube 页面,输入邮箱地址和密码,点登录,才能进邮箱。自动登录插件的作用是,你打开页面,它自动帮你填好凭据并登录,你直接就看到收件箱了。
原理其实不复杂。Roundcube 有个 auto_login 插件,你配置好默认的 IMAP 用户名和密码,插件会在页面加载时自动提交登录表单。但这里有个安全问题:密码是明文存在配置文件里的。所以这套方案只适合个人使用,或者在内网环境下用。如果你要把 Roundcube 暴露到公网,建议加一层 HTTP 基本认证,或者用 Cloudflare Access 做访问控制,不然别人知道你域名就能直接进你邮箱。
配置方法是在 Roundcube 的配置文件里启用插件,然后设置默认凭据。具体来说,在 config/config.inc.php 里加上 $config['plugins'] = array('auto_login');,然后在 plugins/auto_login/config.inc.php 里填上你的 IMAP 服务器、用户名和密码。IMAP 服务器填 QQ 邮箱的 imap.qq.com,端口 993,用户名是你的 QQ 邮箱地址,密码是 QQ 邮箱的授权码——注意不是 QQ 密码,是授权码,需要在 QQ 邮箱设置里生成。
3.4 Docker Compose 编排文件的关键参数
Docker Compose 文件是整套方案的核心,我贴一下关键部分并解释每个参数的作用:
解释几个关键点。restart: unless-stopped 保证容器挂了会自动重启,除非你手动停掉。depends_on 确保数据库先启动,Roundcube 后启动,不然 Roundcube 连不上数据库会报错。volumes 把配置和插件目录挂载出来,方便你修改配置和安装插件,不用每次都进容器里改。数据库的 volumes 把数据持久化到宿主机,容器删了数据还在。
注意:
ROUNDCUBEMAIL_SMTP_PASS和数据库密码这些敏感信息,建议用.env文件管理,不要把明文写在 Compose 文件里。如果你把代码传到 Git 仓库,这些密码就泄露了。我一般会加一个.env文件,然后在 Compose 里用${VAR}引用。
4. 完整实操流程与核心环节实现
4.1 服务器环境准备与 Docker 安装
我用的是一台 2 核 2G 的 VPS,跑这套服务绰绰有余。系统是 Ubuntu 22.04,如果你用其他发行版,命令略有不同,但思路一样。先更新系统包,然后安装 Docker 和 Docker Compose。
安装完成后,验证一下版本:
如果显示版本号,说明安装成功。接下来建一个工作目录,我一般放在 /opt/roundcube,你可以根据自己的习惯来。
然后创建 docker-compose.yml 文件,把前面那段配置贴进去,记得把密码和 API 密钥换成你自己的。创建 .env 文件,把敏感信息放进去:
然后在 Compose 文件里用 ${DB_PASSWORD} 这样的变量引用。这样即使你把 Compose 文件分享出去,密码也不会泄露。
4.2 数据库初始化与 Roundcube 首次启动
Compose 文件准备好之后,先启动数据库容器:
等几秒钟,让数据库完成初始化。你可以用 docker-compose logs db 查看日志,看到 ready for connections 就说明数据库起来了。然后启动 Roundcube:
Roundcube 首次启动会自动建表,你可以在日志里看到建表的过程。等它启动完成后,访问 http://你的服务器IP:8080,应该能看到 Roundcube 的登录页面。如果页面打不开,检查一下防火墙有没有放行 8080 端口,或者容器有没有正常启动。
这个命令可以看到所有容器的状态,Up 表示正常运行,Exit 表示挂了,需要看日志排查。
4.3 QQ 邮箱 IMAP 授权码获取与配置
QQ 邮箱的 IMAP 不是默认开启的,需要手动打开并生成授权码。登录 QQ 邮箱网页版,进入“设置”->“账户”,找到“IMAP/SMTP 服务”,点击开启。它会让你发一条短信验证,验证通过后会给你一个授权码,这个授权码就是你在 Roundcube 里配置 IMAP 时要用的密码。
拿到授权码后,在 Roundcube 的登录页面输入你的 QQ 邮箱地址和授权码,应该就能登录了。登录后,你可以在“设置”->“首选项”->“服务器设置”里确认 IMAP 和 SMTP 的配置是否正确。IMAP 服务器是 imap.qq.com,端口 993,SSL 加密;SMTP 服务器是 smtp.resend.com,端口 465,SSL 加密。
提示:QQ 邮箱的授权码只显示一次,记得保存好。如果忘了,只能重新生成,旧的授权码会失效。
4.4 自动登录插件的安装与调试
自动登录插件需要手动安装。Roundcube 的插件目录在容器里的 /var/roundcubemail/plugins,我们已经在 Compose 文件里把它挂载到了宿主机的 ./roundcube/plugins。你可以从 GitHub 下载 auto_login 插件,解压到 ./roundcube/plugins/auto_login 目录。
然后修改 Roundcube 的配置文件 ./roundcube/config/config.inc.php,启用插件:
接着在 ./roundcube/plugins/auto_login/config.inc.php 里配置默认凭据:
保存后重启 Roundcube 容器:
再访问 Roundcube 页面,应该就直接进邮箱了,不用再输密码。如果没生效,检查一下插件目录的权限,确保容器内的 PHP 进程能读取配置文件。我遇到过权限问题,插件目录是 root 所有,PHP 进程跑在 www-data 用户下,读不了配置,改成 755 权限就好了。
4.5 Nginx 反向代理与 HTTPS 配置
直接暴露 8080 端口不太优雅,而且没有 HTTPS,浏览器会提示不安全。我用 Nginx 做反向代理,顺便配上 Let's Encrypt 的免费证书。如果你有 Cloudflare,也可以直接用 Cloudflare 的 SSL,更省事。
Nginx 配置大概是这样:
证书用 certbot 申请,命令是 certbot --nginx -d mail.yourdomain.com,它会自动帮你配置好证书和续期。如果你用 Cloudflare,把域名解析到 Cloudflare,开启代理模式,然后在 Cloudflare 的 SSL/TLS 设置里选“完全”模式,源站用自签名证书或者 Let's Encrypt 都行。
注意:如果你用 Cloudflare 代理,记得在 Cloudflare 的 DNS 设置里把
5. 常见问题与排查技巧实录
5.1 发信失败与垃圾箱问题排查
发信失败最常见的原因是 SPF 和 DKIM 没配好。你可以用 dig 命令检查你的 DNS 记录:
看看返回的记录里有没有 Resend 提供的值。如果没有,说明解析没生效,等几分钟再试。如果记录对了但发信还是进垃圾箱,检查 DMARC 策略是不是太严了。我一开始设了 p=reject,结果自己发给自己的测试邮件都被拒了,改成 p=none 就正常了。
还有一个坑是 Resend 的域名验证状态。你添加域名后,Resend 会显示“Pending”状态,等 DNS 记录生效后才会变成“Verified”。如果一直是 Pending,检查一下 DNS 记录有没有填错,特别是 DKIM 记录,主机名和值都不能错。
5.2 IMAP 连接超时与授权码失效
IMAP 连接超时一般是网络问题或者授权码错了。先检查 QQ 邮箱的 IMAP 服务有没有开启,然后确认授权码是不是最新的。QQ 邮箱的授权码有时候会失效,特别是你改了 QQ 密码之后,授权码会自动失效,需要重新生成。
如果 Roundcube 报“无法连接到 IMAP 服务器”,可以在容器里用 telnet 测试一下:
如果连不上,说明是网络问题,检查服务器的出站防火墙规则。如果连上了但登录失败,那就是授权码的问题。
5.3 自动登录不生效的几种可能
自动登录插件不生效,常见原因有几个。第一,插件没启用,检查 config.inc.php 里的 $config['plugins'] 数组有没有包含 auto_login。第二,插件配置文件路径不对,Roundcube 只会读取 plugins/auto_login/config.inc.php,你放在别的地方它不认。第三,权限问题,前面说过,插件目录的权限要确保 PHP 进程能读。第四,缓存问题,Roundcube 有缓存机制,改了配置后重启容器,有时候还要清一下缓存目录。
我遇到过一次,插件配置都对,但就是不生效,后来发现是 Roundcube 的会话缓存没清。进容器把 /var/roundcubemail/temp 目录清空,重启就好了。
5.4 Docker 网络不通与容器间通信故障
Docker 容器之间通信,用的是容器名作为主机名。比如 Roundcube 连数据库,主机名填 db,而不是 localhost 或 127.0.0.1。如果你填了 localhost,它会连到容器自己,而不是数据库容器,肯定连不上。
如果容器之间 ping 不通,检查一下是不是在同一个网络里。docker network inspect mailnet 可以看到网络里的容器列表。如果容器不在同一个网络,用 docker network connect 把它加进去。另外,Docker 的默认网桥有时候会有 DNS 解析问题,重启 Docker 服务可以解决大部分网络故障。
提示:如果你改了 Compose 文件里的网络配置,记得先
docker-compose down再docker-compose up -d,不然旧的网络配置不会生效。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 发信进垃圾箱 | SPF/DKIM 未配置 | dig TXT 检查记录 |
补全 DNS 记录,等生效 |
| IMAP 连接超时 | 授权码失效 | 容器内 telnet 测试 | 重新生成授权码 |
| 自动登录不生效 | 插件未启用 | 检查 config.inc.php | 启用插件并重启 |
| 数据库连接失败 | 主机名填错 | 检查 Compose 配置 | 主机名改为容器名 |
| 页面打不开 | 端口未放行 | 检查防火墙规则 | 放行对应端口 |
| 容器频繁重启 | 内存不足 | docker stats 查看 |
增加服务器内存 |
6. 我踩过的坑与实操心得
这套方案我前后折腾了大概一周,中间踩了不少坑,挑几个有代表性的说说。
第一个坑是 Resend 的 SMTP 用户名。我一开始以为用户名要填自己的邮箱地址,结果一直认证失败。后来翻文档才发现,Resend 的 SMTP 用户名固定是 resend,不是你的邮箱。这个细节文档里写得不明显,我找了半天才找到。
第二个坑是 QQ 邮箱的授权码。我一开始用 QQ 密码去登录 IMAP,怎么都登不上。后来才知道 QQ 邮箱的 IMAP 需要单独的授权码,而且授权码在改密码后会失效。所以如果你改了 QQ 密码,记得重新生成授权码并更新 Roundcube 的配置。
第三个坑是 Docker 的网络配置。我一开始把数据库端口映射到了公网,想着方便调试,结果第二天就发现数据库被扫了,还好密码够复杂没被攻破。后来赶紧把端口映射去掉,只让容器内部通信。这个教训告诉我,数据库这种东西,能不开公网就绝对不开。
第四个坑是自动登录插件的权限问题。插件目录是 root 所有,PHP 进程跑在 www-data 用户下,读不了配置文件,导致自动登录一直不生效。后来把插件目录权限改成 755,问题解决。这种权限问题在 Docker 里很常见,因为容器里的用户和宿主机的用户可能不一致,挂载目录的时候要注意权限。
最后分享一个小技巧:如果你想让 Roundcube 支持多域名邮箱,可以在配置文件里设置多个 IMAP 服务器,让用户在登录时选择。不过对于个人使用来说,一个域名就够了,配多了反而复杂。另外,Roundcube 的界面语言可以在配置文件里设成中文,默认是英文,改一下 $config['language'] = 'zh_CN'; 就行。
这套方案跑了大半年,整体很稳定。发信走 Resend,基本没进过垃圾箱;收信走 QQ 邮箱,垃圾邮件过滤很省心;Roundcube 的网页界面也够用,自动登录让我打开浏览器就能看邮件,体验跟用 Gmail 网页版差不多。如果你也在找一套自建邮箱的方案,可以参考我这个思路,根据自己的需求调整。