
直接说结论宝塔面板部署的 Dify 社区版SMTP 邮件配置保存后显示发送成功、却收不到邮件绝大多数问题不在 Dify 应用层而在容器网络层和宝塔面板的端口/SSL 策略上。我在本地和服务器上分别部署过 Dify 1.6 和 1.10 版本踩过不少坑这篇就把排查路径完整写出来按步骤走大概率能解决。先说下我遇到这个问题的背景用宝塔面板Linux 版在一台 2C4G 的云服务器上通过 Docker Compose 方式部署了 Dify配置了 QQ 邮箱的 SMTP 授权码测试发送时 Dify 界面提示发送成功但 QQ 邮箱和备用收件箱里始终没有邮件。翻日志发现worker容器里有一条smtplib.SMTPServerDisconnected: Connection unexpectedly closed的报错这基本锁定了问题出在容器到 SMTP 服务器的网络链路上而不是配置本身。1. 先搞清楚 Dify 邮件发送的架构不是配置一下就能通的事很多人把 Dify 的邮件配置理解成填个 SMTP 地址、端口、账号、密码点保存就完事实际上 Dify 的邮件发送链路至少经过三层Dify API 容器接收你在 UI 里填写的 SMTP 配置并把它写入数据库。Dify worker 容器真正执行邮件发送任务的组件它负责通过 SMTP 协议把邮件交给邮件服务商。Docker 宿主机网络worker 容器发出的网络请求需要经过 Docker 的 NAT 网络、宿主机的 iptables 规则再到达公网 SMTP 服务器。如果你的部署方式是宝塔面板的 Docker 管理器直接拉取 Dify 的 docker-compose.yml 并启动那么Dify 容器默认使用 bridge 网络模式容器出网依赖宿主机的 NAT。宝塔面板默认的安全策略可能会在 iptables 层面拦截部分出站连接但更常见的坑是——容器内的 DNS 解析失败导致 smtp.qq.com 解析不到 IP连接直接中断。还有一个容易忽略的点Dify 的 UI 测试发送按钮和 worker 实际发送邮件使用的是两套逻辑。UI 测试时只是校验配置能否通过 API 层验证真正发出邮件是在 worker 里异步执行的。所以 UI 提示成功不代表 worker 能成功发信。这一点很多人都会误判。排查时要同时看三个地方的日志Dify API 容器日志docker logs dify-api -fDify worker 容器日志docker logs dify-worker -fDify 的 PostgreSQL 数据库里smtp_settings相关表的数据顺带说一句如果你用的是 Dify 1.10 及以上版本配置项里可能还会多出mail_api相关选项但社区版目前主要仍以 SMTP 为主。2. 第一步排查从 Dify 界面报错反推问题根源在 Dify 后台的设置 - 邮件配置页面填好 SMTP 信息后点测试发送如果你能看到明显的报错提示那反而好办。我总结了几种最常见的报错及其对应的问题报错信息问题根源Name or service not known容器 DNS 解析失败worker 容器无法解析 SMTP 域名Connection unexpectedly closed网络链路被中断常见于 TLS 握手被阻断或服务器防火墙拦截authentication failedSMTP 账号密码错误或者未使用授权码而使用了邮箱密码timed out宿主机出网端口被封或邮件服务商限制了主机 IP 的 TLS 连接这里有个很关键的判断如果报错是Name or service not known先不要折腾 Dify 配置进容器里测一下 DNS 解析docker exec -it dify-worker cat /etc/resolv.conf docker exec -it dify-worker getent hosts smtp.qq.com如果getent hosts没有任何输出说明容器的 DNS 解析有问题。在宝塔面板环境下通常是 Docker 守护进程的 DNS 配置没写好。你可以检查一下宿主机的/etc/docker/daemon.json确认是否设置了有效的 DNS 服务器{ dns: [8.8.8.8, 114.114.114.114] }设置完记得重启 Dockersystemctl restart docker重启后再进容器测试解析一般就能通。这里要说一句实践体会8.8.8.8在国内有时连接性一般加上114.114.114.114或阿里云的223.5.5.5更稳。如果报错是Connection unexpectedly closed问题大概率在 TLS 握手阶段后面第四节会详细展开。3. 配置项逐项检查端口、加密方式、授权码一个都不能错排除网络层问题后再回头检查配置项本身。虽然听起来简单但我敢说八成的人栽在这里尤其这几个小细节UI 上不显眼但致命SMTP 端口选择要和加密方式配套QQ 邮箱SMTP 服务器smtp.qq.comSSL 加密用 465 端口STARTTLS 则用 587 端口。163 邮箱smtp.163.comSSL 用 465STARTTLS 用 587。Gmailsmtp.gmail.comSSL 用 465STARTTLS 用 587。国内服务器直连大概率不通需要额外处理。Dify 里有一个加密方式下拉框选SSL还是STARTTLS必须和端口匹配。如果你选了 465 端口但加密方式却填成 STARTTLS某些邮件服务商会直接断开连接报出Connection unexpectedly closed。必须使用授权码而不是邮箱密码 国内邮箱QQ、163在开启 SMTP 服务后会给一个 16 位的授权码。Dify 密码框里要填的是授权码不是登录密码。填了登录密码后大概率返回的报错是SMTPAuthenticationError: Username and Password not accepted。发件人邮箱要和 SMTP 账号一致 有的朋友 SMTP 账号填testqq.com发件人邮箱却写了blog163.com这在某些邮箱服务商看来是非法发信行为直接拒绝。Dify 界面里的发件人邮箱地址和 SMTP 账号邮箱必须保持一致。如果这三项都确认过了接着做一次裸测也就是不通过 Dify直接用本机 Python 脚本测试 SMTP 连通性。这个建议放在配置完成后先执行避免每次改配置后都要重启 Dify 容器浪费时间import smtplib from email.mime.text import MIMEText smtp_server smtp.qq.com smtp_port 465 username 你的邮箱qq.com password 你的授权码 msg MIMEText(test from python, plain, utf-8) msg[Subject] SMTP test msg[From] 你的邮箱qq.com msg[To] 收件箱example.com try: server smtplib.SMTP_SSL(smtp_server, smtp_port, timeout10) server.login(username, password) server.sendmail(username, [收件箱example.com], msg.as_string()) server.quit() print(sent ok) except Exception as e: print(failed:, e)在宿主机上跑这个脚本如果能收到邮件说明 SMTP 本身没问题问题在 Dify 容器环境如果脚本也发不出去那就是宿主机网络层的事继续往下看。4. 宝塔面板环境下的深层排查容器日志与安全软件三重门如果配置没问题裸测也通过但 Dify 里发信还是失败这时候必须看 worker 容器的日志。一定要用下面这个命令加上--tail参数避免刷屏docker logs dify-worker --tail 200 -f日志里如果出现SMTPServerDisconnected: Connection unexpectedly closed我建议按下面三步排查4.1 宝塔面板的防火墙系统防火墙出站规则宝塔面板的安全页面默认会放行22、80、443这几个入站端口但对出站端口没有严格限制。理论上 Docker 容器出网的流量不会被宝塔安全策略拦截但如果你在服务器上额外装了云防火墙比如腾讯云/阿里云的防火墙就得检查是否把出站端口限制得太死。比如允许出站只开放了 80/443而 SMTP 的 465 或 587 被拦住连接就会半路中断。验证方法很简单——在宿主机上用nc或telnet测试 SMTP 服务器的端口连通性nc -zv smtp.qq.com 465如果宿主机通但容器不通那问题出在 Docker 网络或宿主机到容器再往外走的链路上。也可以直接从容器里测试docker exec -it dify-worker sh -c nc -zv smtp.qq.com 465注意worker 容器是基于 Python 镜像构建的里面不一定装了nc没有的话可以用timeout 5 bash -c echo /dev/tcp/smtp.qq.com/465 echo open来测。4.2 TLS/SSL 握手被中断需要检查 OpenSSL 版本兼容性Connection unexpectedly closed还有一个高频原因是容器内的 OpenSSL 和邮件服务商要求的 TLS 版本不兼容。QQ 邮箱现在优先要求 TLS 1.2如果容器基础镜像的 OpenSSL 版本太老握手阶段就会被服务商主动断开。Dify 官方镜像一般基于较新的 Python 基础镜像构建但如果你用的是老版本 Dify比如 1.5 或更早或者自己魔改了 Dockerfile就可能触发这个问题。建议先看下 worker 容器里的 Python 和 OpenSSL 版本docker exec -it dify-worker python -c import ssl; print(ssl.OPENSSL_VERSION) docker exec -it dify-worker python --versionOpenSSL 版本至少在 1.1.1 以上比较保险如果是 1.0.x建议升级 Dify 版本或换用一个较新的 Python 镜像重建 worker 服务。4.3 宝塔面板的系统加固/宝塔 WAF/云锁之类软件很多宝塔用户会顺手装系统加固或宝塔安全防护插件。这些插件默认的防暴力破解规则会扫描所有新发起的连接如果发送频率过高可能触发瞬时封禁。虽然 SMTP 发送不像 SSH 爆破那样高频但我在实际排查中确实见过系统加固插件把 worker 容器的连接判定为异常并拦截的情况。排查方法先在宝塔面板里临时关闭系统加固插件的防护再触发一次 Dify 测试发送如果突然成功了那就是插件规则的问题。处理方式是添加上白名单规则放行 worker 容器的出站端口。5. 从日志到解决拿一次真实的 Dify QQ 邮箱排错全程复盘理论说了一堆放一段我遇到过的完整排错链路觉得最有参考价值。当时部署完 Dify 1.6 版本UI 测试发送报错了日志显示smtplib.SMTPAuthenticationError: Authentication failed我先检查了配置发现用户把 QQ 邮箱的邮箱密码填进去了而不是授权码。指导他登录 QQ 邮箱网页端在设置 - 账户 - 开启 SMTP 服务里拿到 16 位授权码重新填写后依然报错Connection unexpectedly closed这就很蹊跷了。用宿主机 Python 裸测同样账号和端口却能正常发出。于是我把注意力转向容器环境先在容器里测端口连通性docker exec -it dify-worker sh -c echo /dev/tcp/smtp.qq.com/465 echo open输出open说明端口通。接着查看 worker 容器里的/etc/resolv.conf发现 DNS 配置被宝塔的 Docker 管理器改成了127.0.0.11但是这个地址在 bridge 网络下居然无法正常访问上游 DNS。也就是 DNS 能解析部分域名但解析smtp.qq.com时超时错乱导致 SMTP 握手完成后连接被服务商断开。解决方式很简单直接在 docker-compose.yml 里的 worker 服务段加一行dns:配置services: worker: dns: - 8.8.8.8 - 223.5.5.5然后重新docker compose up -d再测试发送邮件顺利收到。这个案例说明一个很核心的经验Dify 界面的报错信息只是表象真正的排查入口是 worker 容器日志而 worker 容器日志里又只能看到 smtplib 的报错要再往下挖就得进容器逐层验证。没有捷径一步步来反而最快。6. 再深一层宝塔里用 Docker Compose 部署 Dify 时的隐藏配置陷阱如果你的 Dify 不是用宝塔的 Docker 管理器可视化点击创建的而是用docker compose up -d命令行启动的那还有一个很隐蔽的问题docker-compose.yml 里的环境变量覆盖了 UI 配置。Dify 的邮件配置有两种来源一种是在 UI 里配置配置会存入数据库。另一种是在.env或docker-compose.yml中配置环境变量比如MAIL_SERVER、MAIL_PORT、MAIL_USERNAME、MAIL_PASSWORD、MAIL_ENCRYPTION。如果你的.env文件里也填了这些变量并且它们和 UI 配置不一致那么最终实际生效的是环境变量UI 界面只是显示但不一定采用。这是个很容易踩的坑在 UI 里怎么改都正确但发邮件还是失败因为环境变量里的旧值一直在起作用。检查方法docker exec -it dify-worker env | grep MAIL docker exec -it dify-api env | grep MAIL如果环境变量存在且和预期不符要么在.env里改成正确值并重启容器要么直接把相关环境变量注释掉让 UI 配置完全接管。另外在 docker-compose.yml 的 worker 服务里volumes部分如果挂载了错误的配置目录也会导致 worker 读不到最新配置。Dify 的官方 compose 模板里worker和api共享同一个./dify/volumes目录如果目录权限不对比如挂载后 uid/gid 不匹配worker 可能无法访问存储配置的数据库或文件间接导致邮件配置读取异常。这种情况在日志里通常会出现Permission denied相关的记录。7. 写在最后Redis 和 PostgreSQL 状态也可能是隐形元凶这不是故弄玄虚。Dify 的邮件配置在测试发送时要经过 PostgreSQL 读写、Redis 消息队列派发任何一个组件假死都会表现为邮件发不出去。尤其你的服务器内存只有 2G 或 3G 时Docker 容器经常因为内存压力被 OOM Killer 杀掉恢复后 PostgreSQL 和 Redis 虽然活着状态却不健康。建议排查时顺带看一眼这两个容器docker stats --no-stream如果 Redis 容器内存占用长期接近上限或 PostgreSQL 容器有大量重启记录先重启这两个容器docker restart dify-redis dify-db然后再测试邮件发送。我也见过一种情况宝塔面板里同时装了多个网站MySQL 和 PostgreSQL 都占用内存Dify 容器内存频频告急。这种情况下邮件配置永远是看似配置正确、实际发不出去。如果想一劳永逸建议给 Docker 设置资源限制或直接把内存扩容到 4G。我用 2G 内存服务器跑 Dify 时邮件发送问题频繁扩容后整个世界都清净了——这是最简单也最容易被忽略的操作。到了这一步思路已经很清楚了配置项 - 裸测 SMTP - 检查 worker 日志 - 深入容器验证网络链路 - 检查环境变量覆盖 - 检查底层服务健康状态。按这个链路排查下来99% 的宝塔安装 Dify 邮件收不到问题都能定位到具体原因。剩下的 1%我碰到过的是域名没有备案导致的邮件被邮件服务商退信但那已经属于业务层面的问题和 Dify 配置无关了。