Dify 用 Docker 部署,Ollama 跑在宿主机,这套本地知识库环境最磨人的就是 host.docker.internal 报错。教程常让人在 .env 里填 192.168.x.x,结果容器访问宿主机一直超时。与其反复试 IP,不如把报错丢给 Codex 拆解;要让 Codex 有模型可用,我给它接一个 TaoToken 通道,官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end ,注册后创建一把 API Key 即可。
1. 先复盘 Dify 连 Ollama 为什么会栽在 host.docker.internal
1.1 容器和宿主机是两套网络
本地跑 Dify 时,容器里的 Dify 后端看到的网络,和你电脑上ipconfig看到的网络不是同一个。Docker 会给每个容器分配独立的网络命名空间,容器内的localhost指向容器自己,不是你的 Windows 或 Mac。OLLAMA_API_BASE_URL 这个变量,决定了 Dify 后端去请求哪个地址来调用 Ollama 的/api/chat和/api/embed接口。
如果你在.env里写OLLAMA_API_BASE_URL=http://192.168.1.10:11434,等于告诉容器里的进程:去访问你无线网卡上的那个地址。这个地址在宿主机上确实存在,但容器能不能访问到,取决于 Docker 的网络模式。Docker Desktop for Windows 默认用 WSL2 或 Hyper-V 虚拟化,容器跑在轻量虚拟机里,虚拟机网段和物理网卡网段不是一回事,跨网段访问经常时通时断。
1.2 局域网 IP 时灵时不灵的真实原因
当电脑休眠唤醒、切换 Wi-Fi、或者 Docker Desktop 重启之后,宿主机的局域网 IP 可能变化,虚拟交换机路由表也可能重建。你在.env里写死的 192.168.x.x 一旦失配,Dify 就需要重启容器并重新读取变量才能恢复。而host.docker.internal是 Docker Desktop 提供的特殊域名,由 Docker 引擎自动解析到宿主机,不随路由器 DHCP 分配变化,所以它比手写 IP 稳定得多。
如果是在纯 Linux 服务器上跑 Docker,没有host.docker.internal这个域名,通常要改用172.17.0.1这个 docker0 网桥的默认网关地址,或者在docker run和docker compose里显式添加extra_hosts。这些判断正好是 Codex 擅长的事,把它丢给 Codex 去分析,比自己在百科里翻 Docker 网络文档要快。
2. 给 Codex 接通 TaoToken:拿 Key 与 config.toml 落点
2.1 去官网拿 API Key
打开 TaoToken,注册账号后进入控制台的 API Keys 页面,创建一把 Key,先复制下来,后面填到 Codex 的环境变量里。这把 Key 在你的终端里以YOUR_API_KEY占位符形式出现,不要写进任何会提交到 Git 的文件。
TaoToken 在这里的角色是一个统一 API 通道,负责把 Codex 需要的模型推理请求转发到对应模型服务。Dify 和 Ollama 之间的流量仍然只存在于你的电脑内部,TaoToken 不接触也不存储你的 Dify 对话数据、知识库文件或本地文档。它解决的是 Codex 这个分析助手有没有模型可用的问题,不是把 Dify 的请求发到公网。
2.2 修改 Codex 配置文件
Codex CLI 读取~/.codex/config.toml,在这个文件里增加一个模型供应商。打开终端,执行:
mkdir -p ~/.codex codex ~/.codex/config.toml然后把下面这部分追加进去。注意 Base URL 是https://taotoken.net/api,末尾不要加/v1,也别把 UTM 参数带进来。
model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"这里的model_provider字段会把 Codex 默认的模型通道切到 TaoToken。具体模型名称不要照抄网上任何示例,以 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 模型广场里当时列表显示的 ID 为准。如果你用的是 Codex 的桌面端或 IDE 插件,通常在设置里填 Provider Base URL 的地方填https://taotoken.net/api,API Key 填YOUR_API_KEY,模型 ID 从模型广场复制。
2.3 确认 Codex 已经能用
在终端设置环境变量:
export TAOTOKEN_API_KEY=YOUR_API_KEYWindows PowerShell 用户用:
$env:TAOTOKEN_API_KEY="YOUR_API_KEY"然后跑一个最直接的验证:
codex exec "用一句话说明 host.docker.internal 和 127.0.0.1 的区别"如果 Codex 正常返回解释,说明模型通道已经打通。接下来就可以把 Dify 连 Ollama 的报错现场拿给它分析了。
3. 把 host.docker.internal 报错整理成 Codex 能读的现场信息
3.1 先收齐四份现场材料
Codex 排障的前提是拿到准确输入。在发给 Codex 之前,先准备好以下内容,每一样都有明确用途:
- Dify 项目根目录下
docker/.env文件,重点看CUSTOM_MODEL_ENABLED和OLLAMA_API_BASE_URL两行有没有写对; docker-compose.yml里 api 服务和 worker 服务的网络配置,确认它们没有绑定在某个固定容器网络上;- 终端执行
docker ps的输出,确认 Dify 的容器都在运行; - 终端执行
docker inspect dify-api -f "{{.HostConfig.NetworkMode}}",看容器用了 bridge 还是 host 网络;顺便把操作系统信息、Docker Desktop 用的还是 WSL2 后端也告诉 Codex。
准备好之后,把这几份内容的文本直接粘贴给 Codex。不要让 Codex 代跑命令去连 Docker daemon,你只要在本地终端执行上面的命令,然后把输出贴回对话里就行。
3.2 给 Codex 一个结构化的排障提示
提示词可以参考这样写,把关键信息、当前配置、想让它判断的方向都说清楚:
当前环境:Windows 11 + Docker Desktop + WSL2 Ollama 跑在宿主机,监听 11434 端口。 Dify 通过 Docker 部署,位于 dify 项目的 docker 目录。 .env 中目前配置: CUSTOM_MODEL_ENABLED=true OLLAMA_API_BASE_URL=http://192.168.1.10:11434 在 Dify 的 设置 -> 模型供应商 -> Ollama 里测试连接,报连接失败。 这是 .env 全文、docker-compose.yml 相关片段、docker ps 输出: [粘贴内容] 请帮我判断: 1. 这个报错更可能是容器网络模式的问题,还是 WSL2 NAT 导致访问不到宿主机; 2. OLLAMA_API_BASE_URL 到底应该写什么; 3. 是否需要检查 Ollama 自己的监听地址。Codex 拿到这些信息后,通常会从两个方向给结论:一是让你把 URL 改成http://host.docker.internal:11434;二是让你去检查 Ollama 的OLLAMA_HOST是否绑定在0.0.0.0上,否则host.docker.internal解析成功也会被拒绝连接。
3.3 Codex 判断逻辑里容易忽略的一点
很多人以为host.docker.internal是万能药,改了就能通。Codex 如果看到 Ollama 默认安装在 Windows 上,通常会提醒你:Ollama 服务默认只监听127.0.0.1,意思是只有本机程序能访问,Docker 容器里的 Dify 即使解析到宿主机地址,也会收到拒绝连接。这时候要在 Windows 的环境变量里加一个OLLAMA_HOST=0.0.0.0,重启 Ollama,再回到 Dify 测试。这个判断比单纯换 IP 更贴合报错本质。
4. 按 Codex 的结论改 OLLAMA_API_BASE_URL 并重启容器
4.1 正确的 .env 两行配置
在 Dify 项目的docker/.env文件末尾,最终应该看到这两行:
CUSTOM_MODEL_ENABLED=true OLLAMA_API_BASE_URL=http://host.docker.internal:11434注意是http://host.docker.internal:11434,不是host.docker.internal:11434。有些教程把http://省了,Dify 读取后拼接请求时也可能能自动处理,但保险起见按带协议头的格式写。如果你在 Linux 服务器上部署,Codex 可能会建议改成http://172.17.0.1:11434或使用extra_hosts映射。
4.2 为什么必须 down/up 而不是 restart
.env变量是在执行docker compose up时被读取进容器环境变量的,docker compose restart只重启进程,不会重新读取.env。改了配置后,必须做一次完整的重建:
cd dify/docker docker compose down docker compose up -ddown会停止并移除容器,up -d重新创建容器并读取新的变量。执行完之后等十几秒,让后端服务完成启动,再打开 Dify 界面去测试模型连接。
4.3 回到 Dify 设置里做二次验证
打开 Dify 界面,进入「设置」-「模型供应商」,找到 Ollama 供应商。如果之前已经添加过,直接点模型名称旁边的设置图标,重新填入模型 ID,比如deepseek-r1:8b,再点测试。如果测试成功,说明 Dify 到 Ollama 那条链路已经通。
如果还失败,把新的报错原文贴回给 Codex,让它继续缩小范围。通常下一步是看docker logs dify-api --tail 50输出,这段日志也只有你自己终端能拿到,贴回对话给 Codex 看即可。
5. 排障之外的注意事项:Embedding 模型与端口监听
5.1 OLLAMA_HOST 没放开导致的连接拒绝
很多人在 Dify 里配好 Ollama 后,Dify 报错不是超时而是 connection refused。这时候问题不在 Dify,也不在.env里的域名,而在 Ollama 服务本身。Ollama 在 Windows 上安装后,默认只监听本机回环地址127.0.0.1,这就等于说:只有宿主机自己可以访问,Docker 容器里的进程一律拒绝。
解决方式是给宿主机添加一个系统环境变量:
OLLAMA_HOST=0.0.0.0添加后必须完全退出 Ollama 再重新打开,因为 Ollama 只在启动时读取这个变量。重新运行ollama list确认服务在线,再回 Dify 点测试。这一步和host.docker.internal的域名解析是两件独立的事,前者是路由能不能到,后者是到了之后 Ollama 给不给你开门。
5.2 Embedding 模型别用对话模型凑合
原文里那个案例还有一个值得注意的坑:知识库召回效果差,不一定是 Dify 配置问题,而是 Embedding 模型选错。deepseek-r1是对话模型,它的职责是生成回复,不是把文本转成向量。拿来当 Embedding 用,语义相似度算不准,知识库匹配结果自然飘。
本地部署推荐单独从 Ollama 拉一个专业的 Embedding 模型:
ollama pull bge-m3拉取完成后,在 Dify 的设置-模型供应商-Ollama 里,新增一个类型为 Embedding 的模型,模型 ID 填bge-m3。同一个 OLLAMA_API_BASE_URL 就能同时服务对话模型和 Embedding 模型,不用额外配置。Codex 在这一步也可以帮你对比bge-m3、nomic-embed-text对中文知识库的适配差异。
6. 跑通之后:控制台对账与后续用法
配置保存后,先用同一把 Key 证明整条链路没有隐患。打开 TaoToken 模型对话 发一条测试消息,确认你复制到 Codex 里的模型 ID 没有填错。如果你打算长期用 Codex 做项目级排障,可以在 Coding Plan 里看套餐是否覆盖你的调用量。
Key 的管理统一在 控制台 API Keys 页面,用完可以随时吊销重建。如果你之后想让 Claude Code 也走同样的通道,环境变量的对照写法可以参考 TaoToken 接入文档。
现在再遇到 Dify 连 Ollama 报错,步骤就清晰了:先让 Codex 读一遍.env和 docker 网络信息,再按它的结论改OLLAMA_API_BASE_URL,最后docker compose down/up重建。不用再换第三个 192.168 开头的 IP 碰运气,直接一次性把容器网络和 Ollama 监听地址一起查明白。