1. 部署前先弄清楚的事:Dify跑起来到底需要哪些组件
很多人在第一次接触 Dify 时,都被它那句“开箱即用的 LLM 应用开发平台”给带偏了,以为部署就是一个容器的事。实际用下来你才会发现,Dify 不是一个单进程应用,而是一整套服务的编排组合。它解决的核心问题,是让开发者不用从零去写 Prompt 管理、上下文拼接、知识库检索、工作流编排、Agent 调度这些重复轮子,直接把精力放在业务逻辑上。社区版免费、开源、可私有化部署,适合个人学习、企业内部工具、AI 应用原型的快速验证,以及一切对数据隐私有要求的场景。
如果你只是想在本地电脑上跑个 Demo,或者在一台云服务器上给团队搭一个 AI 应用平台,这篇部署记录能帮你少踩不少坑。我会把从环境准备、Compose 启动、配置项调整,到本地模型接入、升级报错排查的完整过程都过一遍,包括大量我在实际操作中才意识到的问题——这些在官方文档里往往只是一句带过,但恰恰是决定部署成败的细节。
1.1 拆解 Docker Compose 里的组件清单
先看一眼 dify/docker 目录下的 docker-compose.yaml,你会发现服务列表比想象中长:nginx 负责反向代理和静态资源,web 是前端页面,api 是后端逻辑,worker 是异步任务消费队列,db 是 PostgreSQL,redis 承担缓存和队列中间件,weaviate 或 qdrant 负责向量检索,还有 ssrf_proxy 用于代理外部 HTTP 请求、sandbox 用于隔离代码执行、plugin_daemon 是较新版本里的插件守护进程。不同版本的服务列表会有差异,以你下载的 docker-compose.yaml 实际内容为准。
这一整套组件各司其职:你上传一份文档,api 会把文档交给 worker 去切分,切分后用 embedding 模型生成向量,写入向量库;有人提问时,api 先从向量库召回相关片段,再把片段拼进 Prompt,最后调用大模型生成回答。任何一个环节出问题,表现出来就是“知识库报错”或“对话异常”。所以部署 Dify 之前,心里要有这张组件拓扑图,排查问题时才知道该去看哪个容器的日志。
1.2 硬件与操作系统的底线
Dify 官方推荐 2C4G 起步,但我的实测体会是:2C4G 能跑,启动过程明显偏慢,知识库文档一多,worker 的处理速度会让人等到怀疑人生。个人学习可以接受,生产或团队使用建议至少 4C8G,磁盘预留 20GB 以上(镜像加数据很容易超过 10GB),因为模型调用和向量数据会持续增长。
操作系统方面,Debian 11/12、Ubuntu 22.04/24.04 最省心,CentOS 7 因为 Docker 版本太老,装新版 Docker 会比较折腾。Windows 用户建议用 Docker Desktop,并且让 WSL2 作为后端,同时把部署目录放在 WSL2 的文件系统内而不是 /mnt/c 下,否则文件读写性能和路径映射会带来非常诡异的权限问题。macOS 上用 Docker Desktop 基本没什么坑,唯一要注意的是 Apple Silicon 机器上某些镜像如果没有 arm64 版本,Docker 会走模拟层,性能打折扣。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Docker 环境准备与 Compose 目录初始化
很多部署失败其实不是 Dify 的问题,而是 Docker 环境本身就处于“看似能用、实际残缺”的状态。我在帮同事排查时发现,最常见的两种情况:一是 docker 命令存在,但 docker compose(注意,是带空格的插件,不是老式的 docker-compose)没装;二是 Docker 服务没有设置开机自启,服务器重启后所有容器全部停摆。
2.1 先用这两条命令确认底座
不管你是哪种系统,先执行下面两条命令,确定 Docker 和 Compose 插件都可用:
bash复制docker --version
docker compose version
如果你的 Linux 服务器还没有 Docker,可以按官方源安装:
bash复制sudo apt update
sudo apt install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
这里我踩过一个坑:某些云主机预装的 Docker 版本很老,不支持 Compose v2 语法,直接跑 docker compose 会提示找不到命令。解决办法就是把 Docker 升级到较新版本,别在旧版上浪费时间。
2.2 Clone 源码并锁定版本号
Dify 的部署目录不在根目录,而是在仓库里的 docker/ 子目录,这一点很关键。操作流程:
bash复制git clone https://github.com/langgenius/dify.git
cd dify/docker
cp .env.example .env
这里强烈建议不要直接用 main 分支。main 分支是开发分支,镜像标签和配置项随时可能变化,今天能启动,明天 pull 一个新镜像可能就起不来了。正确做法是先用 git tag 看看最新的稳定版本号,然后 checkout 到具体版本:
bash复制git fetch --tags
git tag -l | sort -V | tail
git checkout 1.10.0
锁定版本之后,后续升级也是按版本序号走,方便对照官方 Release Notes。我就见过有人一直追 main 分支,某次更新后 API 容器启动失败,查了半天才发现是对应镜像的配置项变更了。这种坑完全可以通过版本锁定来避免。
2.3 首次启动:别被“Up”状态欺骗
执行启动命令:
bash复制docker compose up -d
首次启动会拉取全部镜像,包含沙箱、代理、向量库等,镜像总量大约 3-4GB,耐心等。启动完成后,很多人的习惯是看一眼 docker compose ps,看到 Up 就觉得万事大吉。实际上 Up 只代表容器进程没退出,不代表服务已经就绪。Dify 的 api 和 worker 容器在启动时还需要执行数据库迁移、初始化表结构,这段时间内访问页面会看到 502 或连接拒绝。
正确判断方式是看容器状态里的 healthy 标记:
bash复制docker compose ps
web 和 api 从 Up 变成 Up (healthy) 通常需要 1 到 3 分钟,视机器性能而定。如果一直停留在 Up,用日志看启动进度:
bash复制docker compose logs --tail=100 api
看到类似 “Startup process completed” 或 http server 成功监听的日志,再打开浏览器访问。首次访问 http://服务器IP/install 会进入管理员初始化页面,设置管理员邮箱和密码,这一步完成之后才算是真正跑起来了。
2.4 默认 80 端口被占用怎么处理
Dify 默认通过宿主机的 80 端口对外提供服务。如果你的机器上已经跑了 Nginx、Apache、Jenkins 或其他 Web 服务,docker compose up -d 会因为端口冲突直接失败,日志里报 port is already allocated。
解决办法是改 docker-compose.yaml 中 nginx 服务下的端口映射,把宿主机端口改成你想要的,比如:
yaml复制ports:
- "8080:80"
这里只需要改冒号左边的宿主机端口,容器内部的 80 不要动。改完再执行 docker compose up -d 重建。注意以后所有访问地址都要带上新端口,比如 http://服务器IP:8080。
Windows 上还有一个隐蔽问题:即使端口看起来没被监听,有时候 netstat 查不到占用,但启动仍然失败。这通常是 Windows 的 HTTP.sys 或 IIS 在悄悄占用 80。这时候别纠结,直接把宿主端口改成 8080 或 8000,绕开它。
3. 部署前必须改的配置项:SECRET_KEY、存储与向量库
.env 文件是整个部署的核心配置入口,里面变量非常多,但真正需要你在首次启动前改的,其实就几个。剩下的可以等界面跑通后再慢慢调。
3.1 SECRET_KEY 的事故级别问题
SECRET_KEY 是 Dify 用来对用户会话、API Token 进行签名和校验的密钥。.env.example 里给的默认值是一个公开的占位字符串,如果直接用默认值启动,等于把整个系统的会话密钥暴露给了所有看过仓库代码的人——任何人只要知道这个默认值,就可以伪造合法的会话数据。
看看安全的生成方式:
bash复制openssl rand -base64 42
把输出的一长串字符填进 .env:
bash复制SECRET_KEY=你生成的随机字符串
这里有一个非常容易踩的坑:如果系统已经启动过、创建过管理员账号,中途再改 SECRET_KEY,会导致所有已有会话失效,用户全部需要重新登录。更麻烦的是,某些场景下旧的加密数据(比如部分已保存的应用密钥)会解析失败。所以我的建议是:第一次 docker compose up 之前就必须把 SECRET_KEY 定好,之后不再改动。我自己的习惯是专门用一个密码管理器保存这个值,和数据库密码放一起。因为丢了 SECRET_KEY 虽然能重置,但涉及已有用户凭证的重新签发,不是改一下配置就能解决的小事。
3.2 文件存储与向量库选型
.env 里和存储直接相关的变量是 STORAGE_TYPE 和 VECTOR_STORE。
STORAGE_TYPE 默认是 local,表示上传的文档、图片等文件保存在本地的 Docker volume 里。对于个人和中小团队够用,但要注意:Docker volume 默认放在系统盘,数据量大了之后系统盘会被占满。生产环境建议接入 S3 或兼容对象存储(MinIO 也算),在 .env 里配置 STORAGE_TYPE=s3 及相关 Access Key、Bucket 名即可。这样即便容器重建、数据也不会丢。
VECTOR_STORE 默认通常是 weaviate,也可以换成 qdrant、pgvector、milvus 等。对大多数场景,我建议保持默认或者选 Qdrant,因为这两者文档多、问题容易搜到。切换向量库不是在界面上点个按钮,而是要先改 .env 里的 VECTOR_STORE,再重启初始化。如果你的知识库已经导入了大量文档,中途切换向量库等于重新做一遍向量化,全部文档要重新处理和写入。所以向量库的选型,务必在首次导入知识库之前定下来。
下表是我在几个向量库之间纠结时的参考方向:
| 向量库 | 适用场景 | 优点 | 注意事项 |
|---|---|---|---|
| weaviate | 默认方案,小团队 | 部署简单,开箱即用 | 升级时偶发 schema 兼容问题 |
| qdrant | 知识库量大、对检索性能敏感 | 性能好,资源占用可接受 | 需要关注版本兼容 |
| pgvector | 已有 PG 运维体系 | 少一个中间件,DB 统一 | 向量检索能力相对有限 |
| milvus | 大规模生产集群 | 分布式能力强 | 运维复杂度明显更高 |
3.3 模型供应商不在 .env 里配置
不少新手翻遍 .env 找不到 OpenAI API Key 的位置,熬了一晚上以为自己漏看了。其实 Dify 的模型供应商是在后台界面配置的:登录管理员账号后,进入“设置 → 模型供应商”,添加 OpenAI、通义千问、智谱 GLM、讯飞星火等云服务商的 API Key,也可以添加 Ollama 这类本地模型。.env 里只管系统级配置,模型 Key 交给应用层去存,好处是不同工作空间可以绑定不同的模型供应商,权责更清晰。
配置完模型供应商后,记得在“模型供应商”页面点击“测试”按钮,确认能连通再继续创建应用。我见过不少人一上来就配完,也不测试,然后创建应用时才发现 Key 错了、模型名不匹配,排查半天最后只是大小写问题。
4. 本地模型接入实战:通过 Ollama 跑起来
如果你的场景是内网环境,或者不想把对话内容发给外部 API,Ollama 是最顺手的本地模型方案。Dify 在模型供应商里原生支持 Ollama,接入逻辑不复杂,但网络细节和模型名一致性是两个最容易出问题的地方。
4.1 先让 Ollama 在宿主机上正常监听
Ollama 默认只监听 127.0.0.1:11434,如果 Dify 的容器要访问它,必须让 Ollama 监听宿主机局域网地址。启动前设置环境变量:
bash复制export OLLAMA_HOST=0.0.0.0:11434
ollama serve
如果希望长期生效,可以写入 systemd 服务(以 Ubuntu/Debian 为例):
bash复制sudo systemctl edit ollama.service
添加内容:
ini复制[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
然后重启服务:
bash复制sudo systemctl daemon-reload
sudo systemctl restart ollama
这一步漏掉的典型症状是:在 Dify 后台配置 Ollama 模型时测试连通性,页面提示无法连接到 x.x.x.x:11434,但你在宿主机上 curl http://localhost:11434 又正常——问题就是 Ollama 只监听了本机回环地址。另外,如果你希望模型文件换个磁盘存放,同理用 OLLAMA_MODELS 环境变量指定路径,避免系统盘被几 GB 的模型文件撑爆。
4.2 Dify 后台添加 Ollama 供应商
进入 Dify 后台的“设置 → 模型供应商”,找到 Ollama,点击添加。需要填的内容不复杂,核心就两个:Base URL 和模型名称。
Base URL 的填法有三种,按推荐顺序排:
| 方式 | 地址示例 | 适用环境 |
|---|---|---|
| Docker Desktop 内置别名 | http://host.docker.internal:11434 |
Windows/macOS 的 Docker Desktop |
| 宿主机局域网 IP | http://192.168.1.100:11434 |
Linux 服务器,也兼容 Windows/macOS |
| Docker Compose extra_hosts | http://host.docker.internal:11434 |
Linux 下手动添加 host-gateway 映射后 |
Linux 上直接用 host.docker.internal 一般是解析不了的,需要在 docker-compose.yaml 的 api 和 worker 服务下加 extra_hosts:
yaml复制extra_hosts:
- "host.docker.internal:host-gateway"
加完执行 docker compose up -d 才会生效。如果你不想改 Compose 文件,最简单的方案是直接用宿主机局域网 IP,比如 http://192.168.1.100:11434,只要宿主机防火墙放行 11434 端口即可。我个人更喜欢用局域网 IP,因为不依赖 Docker 的额外配置,换机器迁移时逻辑更直白。
模型名称这一项必须和 ollama list 输出里的 TAG 完全一致,包括冒号和版本标签。例如你拉取的是 qwen2.5:7b,那就填 qwen2.5:7b,不能填 qwen2.5 或 qwen2.5:latest。大小写也要注意,Ollama 的模型名是区分大小写的,填错了 Dify 端会报“模型调用失败”。
4.3 容器访问宿主机的网络原理
这里补充一下为什么不能用 localhost。Dify 的 api 容器跑在独立的 Docker 网络里,容器内部的 localhost 指向容器自己,而不是宿主机。所以哪怕 Ollama 就在宿主机上监听 11434,容器里访问 localhost:11434 也是不通的。
理解了这个逻辑,你就会明白为什么排查时先做分层测试:第一步在宿主机上验证 Ollama 连通性,第二步从 api 容器内部验证能否访问宿主机网络,两步都通了,再去 Dify 后台测试模型供应商。有一次我折腾一个小时,最后发现是宿主机防火墙把 11434 的外网访问封了,从容器到宿主机 IP 的连接直接被丢弃。所以如果你用局域网 IP 方案,记得检查防火墙规则,至少在测试阶段把 11434 放行或限定来源 IP。
4.4 embedding 模型和知识库向量化的坑
知识库能力依赖 Text Embedding 模型。你可以给 Ollama 供应商同时配置一个 LLM 模型和一个 Text Embedding 模型。我常用的组合是:对话用 qwen2.5:7b 或 llama3.1:8b,向量化用 bge-m3 或 nomic-embed-text。
容易踩的坑集中在模型名的“同与不同”上。Dify 里配置的模型名必须和 ollama list 完全一致,不能省略 tag;其次,不同 embedding 模型的向量维度不同,比如 bge-m3 是 1024 维,切换模型后向量库里的旧数据和新数据的维度对不上,检索时会直接报错。如果你要换 embedding 模型,最稳妥的做法是删掉旧的知识库、重新创建,让 Dify 用新模型重新处理所有文档,而不是保留旧索引硬撑。
知识库创建后,先上传一两个文档做测试,跑一次“召回测试”,确认检索结果有内容返回,再批量导入剩余文档。如果召回测试直接空白或报错,说明 embedding 链路有问题,这时候再检查就轻松得多。
5. 升级和知识库报错的排查链路:从 internal server error 说起
升级后知识库无法保存,或者修改知识库时提示 internal server error——这是我在 Dify 社区看到的高频问题,也亲身遇到过。这种问题最折磨人,因为错误提示没有具体上下文,前端就给你一个 500。
5.1 为什么升级后容易出这个错
先理解一个背景:Dify 升级时,api 容器启动会自动执行数据库迁移,给现有表加字段、建新表。如果迁移没有完整跑完,或者旧数据里有脏数据,就会导致后续知识库操作时 SQL 报错。而知识库是一个多环节链路的组合——前端调 api,api 写入 PostgreSQL,worker 异步处理文档切分和向量化,向量写入 Weaviate/Qdrant。任何一个环节和当前版本不匹配,都会冒出一个面目模糊的 500。
我复盘过一次具体问题:从旧版本跳到次新版,升级时因为 worker 容器内存不足被 OOM kill,数据库迁移中途中断。之后所有知识库相关操作全部报 500。打开 api 容器日志才发现,是某张迁移表中缺少了预期的列。所以遇到 500,第一反应不是去看前端代码,而是按链路逐层看日志。
5.2 按顺序查四层日志
排查链路按影响面从大到小排列:
bash复制# 第一层:nginx / web,确认请求有没有到达后端
docker compose logs --tail=200 web
# 第二层:api,后端错误基本都会在这打印堆栈
docker compose logs --tail=300 api
# 第三层:worker,异步任务失败只在这能看到
docker compose logs --tail=200 worker
# 第四层:向量库,索引相关错误在这里
docker compose logs --tail=200 weaviate
打开 api 日志后,搜索 ERROR 或 Traceback,定位真正的异常类型。常见的几类:数据库字段缺失,一般是迁移没完成;relation does not exist,说明某个表压根没建出来;embedding 模型调用失败,说明模型供应商配置有问题;向量库 index not found,说明索引和当前版本不匹配。
5.3 数据库迁移未完成时的补救方法
确认是迁移未完成导致的,先不要急着随便执行迁移命令。Dify 的 api 容器在启动时本身会执行迁移逻辑,所以最简单的做法是重启 api 容器让迁移重新触发:
bash复制docker compose restart api
观察启动日志里是否有迁移相关的输出,等 api 变成 healthy 再测试知识库保存功能。
如果重启后依然报同样的错误,再考虑手动进入容器执行迁移。Dify 基于 Flask-Migrate,命令入口是在 api 容器里跑 flask db upgrade,但前提是环境变量已经正确加载。执行方式:
bash复制docker compose exec api flask db upgrade
注意:不同版本实际的迁移命令可能会因为入口脚本差异而不同,执行前先看一眼容器启动日志里的迁移调用方式,别盲目复制网上的命令。手动迁移后还需要重启 api 和 worker:
bash复制docker compose restart api worker
然后再测试。如果日志显示是向量库的索引兼容问题,处理思路就不一样了。旧索引结构和新版本不兼容时,最省事的是备份旧知识库的原始文档,删除原有知识库,让 Dify 重新处理并新建向量索引。这时候你会庆幸当初保留了原始文档——这也是我一直强调“知识库的元数据可以丢,但原始文档一定要留”的原因。
5.4 升级前一定要做的备份和回滚预案
升级前备份这件事,说一百遍都不为过。Dify 的数据主要两部分:PostgreSQL 里的业务数据(用户、应用、知识库元数据、文档列表),以及向量库里的向量索引和本地存储里的原始文件。备份命令可以这样:
bash复制# 备份 PostgreSQL
docker compose exec db pg_dump -U postgres -d dify > dify_backup_$(date +%F).sql
# 备份本地存储的 volume 目录(在容器停止的前提下更安全)
docker compose stop
docker run --rm -v dify_docker_volume_name:/data -v $(pwd):/backup alpine tar czf /backup/dify_storage_$(date +%F).tar.gz /data
docker compose start
恢复的时候,先恢复数据库 dump,再恢复存储卷文件,最后启动容器。升级前把整个 docker/ 目录单独备份一份,包括 .env 和 docker-compose.yaml,是回滚的基础。真出了问题,最坏打算就是把旧代码 checkout 回来,配好旧 .env,恢复数据库和存储卷,一条命令把服务拉回升级前的状态。
另外,不要跳版本升级,比如从 0.6 直接升到 1.10,中间隔着大量数据库结构变更和插件机制变化,大概率会翻车。按版本号逐级升级,每升一级验证一次核心功能,虽然是笨办法,但最可靠。
6. 日常运维里那些容易被忽略的事
部署跑通只是开始,日常维护里的一些细节才是决定体验的关键。这里分享几个我长期使用后总结的经验。
6.1 改了 .env 不生效的真相
docker compose 有一个迷惑行为:如果只改了 .env 文件而不改 docker-compose.yaml,直接执行 docker compose restart 不会重新读取环境变量,因为 restart 只是重启容器进程,不会重建容器。正确做法是执行:
bash复制docker compose up -d
Compose 会比较配置哈希,发现环境变量变化后自动重建受影响的服务。改完 .env 后,记得用 docker compose ps 确认容器的启动时间已经更新。如果发现时间没变,说明容器没有被重建,配置没生效。
另外,修改 .env 中的 SECRET_KEY、数据库密码等敏感项后,不只是重启容器这么简单。数据库密码变了,需要同步改数据库里的用户密码,否则 api 容器能起来但连不上库,反复重启也没用。
6.2 日志轮转和磁盘空间
Dify 运行一段时间后,最容易膨胀的是日志文件和向量数据。api 和 worker 容器在模型调用频繁时,日志量增长很快,而 Docker 默认不限制日志文件大小,几个月不清理,一个容器日志就能吃掉几个 GB 的磁盘。在 docker-compose.yaml 的各个服务下加日志限制:
yaml复制logging:
driver: json-file
options:
max-size: "20m"
max-file: "3"
加完同样要用 docker compose up -d 重建容器才会生效。平时可以用 docker system df 查看镜像、容器、卷的空间占用情况。如果发现磁盘快满,先定位是日志还是数据卷导致的,别盲目清 volume。
6.3 插件、多租户和后续扩展
较新版本的 Dify 社区版引入了插件化架构,很多扩展能力通过“插件市场”安装,不需要动代码。插件也分模型插件、工具插件、Agent 策略插件,安装后需要到模型供应商或工具页面去配置密钥。如果你在后台找不到某个模型供应商,大概率是插件没装,先去插件市场搜一下。这套机制让 Dify 的功能边界扩展变得灵活,但也意味着升级时要检查插件兼容性,否则可能出现插件加载失败导致功能缺失。
多租户和团队协同在社区版新版本里也越来越完整。你可以创建多个工作空间,给不同成员分配不同权限,一个 Dify 实例支撑多条业务线。我自己的做法是:每个业务线一个工作空间,每个工作空间绑定各自的模型供应商和知识库,互不干扰,管理起来清晰很多。
另外一条建议:Dify 部署之后,不要急着建一堆复杂的应用。先在默认工作空间里用最简单的方式跑通一个“聊天助手 + 一个知识库 + 一个 embedding 模型”的最小闭环,确认全链路稳定,再逐步叠加工作流、Agent、插件等能力。我发现很多人一上来就搭复杂工作流,出了问题根本分不清是部署问题还是编排问题,反过来又把锅甩到部署头上。
这套部署和维护流程我已经在多个环境里用过,包括虚拟机、云主机、Docker Desktop,踩过的坑基本都记录在上面了。你在实际操作中如果遇到官方文档里语焉不详的报错,按这条链路去查日志、看迁移、检查模型连通性,大多数问题都能找到方向。
