简介:Dify 官方安装包是从 GitHub 获取的项目主体压缩包,面向希望本地部署、源码研读或二次开发 AI 应用编排平台的开发者,适合用于搭建开发测试环境、了解项目模块组织以及扩展功能。压缩包共 2000 个文件,整体约 20.25MB,其中 Python 源码 1337 个,构成后端逻辑的主要部分;JSON 配置 366 个,CSS 与 JavaScript 文件合计 200 个,承担前端样式与交互资源;另有 Markdown 文档、YAML 部署配置、Shell 脚本及少量 HTML、XML、SQL 等,覆盖配置说明、界面呈现和基础数据等层面。已有 2982 人学习下载,具备一定参考热度。解压后可获得较完整的项目主体,便于查看前后端资源分布、理解模块职责与部署配置关系,也能为本地运行、功能改造和源码分析提供基础材料。对于需要核对官方目录结构、梳理依赖关系或进行二次开发的人而言,这份主体包能减少收集零散文件的时间,也便于对照模块边界开展调试与扩展。
1. Dify 官方安装包:一次up -d卡在向量库上的教训
第一次在自己机器上起 Dify,我照着官方 compose 一路docker compose up -d,前端页面一直转圈,api容器日志里反复刷weaviate:8080 connection refused。查了半小时才发现,新版的 compose 文件把向量库放进了 profiles,不带--profile启动根本不会拉起那个容器。这类坑跟功能一点关系都没有,全在环境里。
Dify 官方安装包的价值也在这儿:它把 api、worker、web、PostgreSQL、Redis、向量库、沙箱这十来个容器编排成一份 compose,镜像、网络、依赖顺序都写好了。需要对齐的只有版本、端口、密钥三样。这篇笔记写给两种人:第一次在自己服务器上装 Dify 的,和装到一半卡住、想找出哪里不对的。下面按装前、装中、装坏、装完验证的顺序走。
2. 装前环境清单:Docker Engine 与 Compose v2 的版本取舍
Dify 的部署形态是「一个 compose 文件带一组容器」,没有任何需要编译的步骤,所以环境准备的重点不是语言运行时,而是容器运行时本身。这一步选错了版本,后面全是白费功夫:用 Compose v1 的docker-compose去跑带profiles的编排文件,会出现某些服务被静默跳过的情况,报错信息还特别含糊。
2.1 宿主机要什么规格:CPU、内存、磁盘的底线
官方给的最低配置是 2 核 4GB,这个数字能跑起来,但只限于「跑起来」——一旦开始建知识库、做文档分段索引,内存立刻见底。我的经验是:
| 项目 | 能跑的下限 | 我建议的配置 | 说明 |
|---|---|---|---|
| CPU | 2 核 | 4 核 | 文档解析、向量化是 CPU 密集型 |
| 内存 | 4 GB | 8 GB 起 | api + worker + 向量库 + PostgreSQL 同时在线 |
| 磁盘 | 20 GB | 50 GB 以上 | 镜像本身占十几个 G,知识库文件另算 |
| 系统 | 主流 Linux 发行版 | x86_64 或 arm64 均可 | 注意镜像架构要和宿主机一致 |
磁盘容易被低估。Dify 全部镜像拉下来,光api和web两个就上 G,加上向量库、PostgreSQL、沙箱,20G 的云盘很快就满,然后你在某次docker compose pull时收到磁盘不足的报错。我一般会把/var/lib/docker单独挂一块盘,或者至少提前确认根分区有 30G 余量。
提示:如果是 4GB 内存的机器硬上,先把 swap 开出来(
fallocate -l 4G /swapfile那一套),能避免 worker 被 OOM killer 直接干掉。但 swap 只是应急用,长期跑还是加内存。
2.2 装 Docker Engine 与 Compose v2:一条不重复调整的安装步骤
Dify 要求 Compose v2,也就是docker compose(中间是空格)这个插件形态,不是老的docker-compose(中间是横杠)。两条命令长得像,但后者在新编排文件上会出问题,这是我最常见的翻车点之一。
以 Debian/Ubuntu 系为例,先清掉发行版源里自带的老包,避免和官方包打架:
# 1) 卸载系统自带的老版本 docker,避免与官方仓库的包冲突 sudo apt-get remove -y docker docker-engine docker.io containerd runc # 2) 安装基础依赖,为添加官方软件源做准备 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings # 3) 添加 Docker 官方软件源(具体源地址与 keyring 路径以官方安装文档为准, # 这一步照着文档逐行粘即可,注意 keyring 不要放到 /etc/apt/trusted.gpg.d) # ... # 4) 安装引擎 + compose 插件 + buildx sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin # 5) 当前用户加入 docker 组,之后不用每条命令都 sudo(需重新登录生效) sudo usermod -aG docker $USER第 4 步里的docker-compose-plugin是关键,装的就是 v2 版 Compose,提供docker compose子命令。第 5 步加组是为了省事,但要注意:加组之后重新登录一次,否则docker ps还是 permission denied;很多人在这一步以为是安装失败,其实只是 shell 没刷新。
装完立刻验证,别等部署时才发现版本不对:
# 引擎版本,关注 Server 那一段是否有输出 docker version --format '{{.Server.Version}}' # Compose 版本,必须是 v2.x(输出形如 Docker Compose version v2.x.x) docker compose version # 确认没有残留的 v1 老命令 which docker-compose如果docker compose version报的是docker: 'compose' is not a docker command,说明插件没装上,回到第 4 步检查包名。如果which docker-compose有输出,说明机器上还躺着 v1 版本,建议直接apt-get remove docker-compose卸掉,防止后面手滑敲错。
启用服务并设置开机自启,服务器重启后容器能自己回来:
sudo systemctl enable --now docker # 容器级别也要在 compose 里带 restart: always,官方文件默认就有2.3 端口与目录:80、5001、3000 该由哪个服务对外暴露
Dify 的 compose 里对外暴露的只有 nginx 一个入口,默认 80(HTTP)和 443(HTTPS)。内部的api走 5001、web走 3000,这些端口不需要也不应该直接暴露到公网。这个层级关系搞清楚,后面端口冲突时才知道改谁。
| 端口 | 谁在用 | 是否需要对外 | 冲突时怎么改 |
|---|---|---|---|
| 80 | nginx 容器 | 是 | 改.env里的EXPOSE_NGINX_PORT |
| 443 | nginx 容器 | 是(用 HTTPS 时) | 改EXPOSE_NGINX_SSL_PORT |
| 5001 | api 容器 | 否 | 不要动,只在内网 |
| 3000 | web 容器 | 否 | 不要动,只在内网 |
| 5432 | PostgreSQL | 否 | 不要动 |
| 6379 | Redis | 否 | 不要动 |
装之前先看一眼宿主机 80 端口有没有人占着,这一步能省你半小时:
# 看 80 和 443 是否被占用,有输出说明已经有服务在监听 sudo ss -lntp | grep -E ':80|:443'云服务器上很常见的情况是预装了 nginx 或 apache 当默认站点,占了 80。这时候有两个选择:停掉宿主机的 web 服务,或者把 Dify 的对外端口改成 8080。我更倾向后者,因为宿主机的 nginx 可能还挂着别的站点。改法在.env里把EXPOSE_NGINX_PORT设成 8080,后面访问就带端口。
目录规划同理,提前想好。Dify 的 compose 会把持久化数据落到docker/volumes/下面(部分版本用命名卷),整个项目目录我一般放在/opt/dify这种位置,而不是某个用户的家目录——家目录权限和磁盘配额都可能成为以后的问题。
3. 拿官方安装包:仓库拉取到 .env 逐项改完
安装包拿法只有一条正路:从官方仓库拉取对应稳定 tag 的源码,用里面的docker目录。第三方打包的「一键脚本」我见过几个,改过端口、加过默认密钥、版本还落后,出了问题没法排查,不建议用。
3.1 拉取官方发布包与 docker 目录结构说明
仓库地址从官方发布页复制,不要凭记忆敲。拉下来之后切到一个明确的稳定 tag,别用主分支——主分支的 compose 文件随时会动,你昨天跑通的步骤今天可能就变了:
# 从官方发布页复制仓库地址,拉取完整仓库 git clone <官方仓库地址> dify cd dify # 切到发布页上标注的稳定 tag,比主分支可控得多 git checkout <稳定版 tag> # 确认目录结构,后面所有操作都在 docker 目录里进行 ls -la docker/docker/目录里最关键的三个东西:docker-compose.yaml(编排定义)、.env.example(环境变量模板)、nginx/(反向代理配置)。.env不存在,需要你自己从模板复制。
这里有个细节值得说:如果你不想用 git,官方发布页上也提供源码压缩包,下载解压效果一样,只是后续升级时要重新下载。用 git 的好处是升级时git pull加git checkout新 tag 就行,改动痕迹也好追溯。
3.2 .env.example 复制成 .env 后必须改的六个参数
这一步是整个安装里最需要人盯的地方。复制很容易,改对不容易。
cd docker cp .env.example .env # 生成一个随机 SECRET_KEY,不要用模板里的默认值 openssl rand -base64 42把生成结果填进.env的SECRET_KEY。然后逐项对照:
| 参数 | 模板默认值 | 我一般改成 | 作用与影响 |
|---|---|---|---|
SECRET_KEY | 示例字符串 | openssl rand -base64 42的输出 | 会话和令牌签名,不换等于把钥匙插门上 |
DB_PASSWORD | 默认弱密码 | 强密码 | PostgreSQL 密码,改了要保证 api 能读到 |
REDIS_PASSWORD | 默认弱密码 | 强密码 | Redis 认证 |
VECTOR_STORE | weaviate | weaviate或qdrant | 向量库类型,改了要同步改 profiles |
EXPOSE_NGINX_PORT | 80 | 80 或 8080 | 对外 HTTP 端口 |
FILES_URL | 空 | http://你的IP或域名 | 文件预览、下载的根地址 |
FILES_URL是最容易被跳过的一项。不填也能启动,上传文件也能成功,但点开预览时链接指向容器内部地址,浏览器打不开。同域部署填对外访问地址即可;如果你前面还有一层反向代理,填代理后的完整地址。
还有几个参数默认留空,只在分离部署时才需要填:CONSOLE_API_URL、CONSOLE_WEB_URL、SERVICE_API_URL、APP_WEB_URL。单机 compose 部署全部留空就行,填了反而容易出跨域问题。
CODE_EXECUTION_ENDPOINT默认指向http://sandbox:8194,这是容器间通信用服务名,不要改成 IP,除非你把沙箱拆出去单独部署了。
注意:
.env里出现密码这类敏感信息,改完把权限收紧:chmod 600 .env。整个目录如果是多人共用,这一条别省。
3.3 向量库选型与 profiles:VECTOR_STORE 怎么改才不返工
Dify 支持多种向量库,compose 文件里通过 profiles 控制哪个服务被拉起。.env里的VECTOR_STORE和 compose 里的服务名必须对上,对不上就是我最开始那个坑:api 起来了,向量库没起,日志一直重连。
先确认你的 compose 文件用的是哪种机制:
# 列出当前 compose 文件里所有服务(不带 profile 时只会列默认服务) docker compose config --services # 看 weaviate 或 qdrant 是否带了 profiles 标记 grep -A3 'profiles' docker-compose.yaml | head -20如果config --services的输出里没有 weaviate,说明它被 profile 藏起来了,启动时要显式带上:
# 显式指定 profile,把向量库一起拉起来 docker compose --profile weaviate up -d # 如果选用 qdrant,则换成对应的 profile 名 # docker compose --profile qdrant up -d选哪个?单机小规模、图省事,weaviate是默认值,改都不用改。数据量上了百万级向量、想要更可控的内存占用,可以换qdrant,代价是要改VECTOR_STORE并带上对应 profile。不管选哪个,改了向量库类型之后,已建的知识库数据不会自动迁移,这一点在建第一个知识库之前就要定下来,别等有数据了再换。
另外提醒一句:compose 里的向量库服务通常没有对宿主机暴露端口,只在容器网络内可达,这是对的,不要为了「方便调试」把它 expose 出去。
4. 首次启动:docker compose up -d 之后的三分钟
配置改完,剩下的动作很少,但等待过程里要看的日志不少。首次启动的耗时基本都花在拉镜像上,容器真正起来的顺序是秒级的。
4.1 拉镜像与启动:镜像体积大时的处理方式
cd /opt/dify/docker # -d 后台启动,--profile 与 .env 里的 VECTOR_STORE 保持一致 docker compose --profile weaviate up -d # 盯着拉取进度,首次会比较久 docker compose ps首次拉取的总量不小,api、web、向量库、沙箱加起来是几个 G 的量级,具体取决于你选的向量库。国内机器上耗时可能到十几分钟甚至更久,这段时间不用反复 Ctrl+C 重来,中断反而会让已完成的分层白拉。我的习惯是先单独把大镜像拉下来:
# 先把体积大的几个拉完,再整体 up,失败重试的成本更低 docker compose pull api worker webpull完成后再up -d,基本就是重建容器的几秒钟。如果某个镜像拉取一直卡住,先确认磁盘剩余空间,df -h看一眼,磁盘满导致的拉取失败报错和网络问题报错长得挺像。
4.2 容器依赖顺序与健康检查怎么看
Dify 的 compose 用depends_on加condition: service_healthy控制顺序,PostgreSQL 和 Redis 健康之后 api 才起,api 起来之后 worker 才起。这套机制大部分时候靠谱,但ps里看到healthy之前,容器实际已经能响应请求了,别被状态吓到。
判断是否真起来,看三个地方:
# 1) 所有容器状态,关注 STATUS 列是否有 unhealthy 或 Restarting docker compose ps # 2) api 日志,正常是 gunicorn 的 worker 启动信息,没有 traceback docker compose logs --tail=100 api # 3) worker 日志,正常是 celery ready 之类的信息 docker compose logs --tail=50 workerapi日志里如果反复出现连不上db或redis的记录,大概率是.env里的密码和实际起容器的密码不一致——你改了.env但 PostgreSQL 的数据卷里还是旧密码。这种情况要么把密码改回去,要么把 db 的数据卷删掉重来(仅在还没有数据时可行)。
worker一直在重启,先看日志最后 20 行,再docker inspect看退出码。退出码 137 就是被 OOM kill 了,跟配置无关,纯粹内存不够。
4.3 初始化管理员账号并跑通第一个应用
容器全绿之后,浏览器打开http://你的IP(改过端口就带端口),会跳到初始化页面,设置管理员邮箱和密码。这一步只在数据库为空时出现,设置完就再也不出现了——密码忘了只能走重置流程,所以第一次就记好。
进去之后别急着导数据,先跑一个最小闭环验证整条链路:
- 在「设置 → 模型供应商」里配一个模型,填入可用的 API Key,点保存。
- 新建一个「聊天助手」应用,模型选刚配的那个。
- 在调试窗口发一句「你好」,能正常回话说明 api、worker、模型调用三段都通了。
- 新建一个知识库,上传一个几页的 PDF,等分段索引完成,再回到应用里挂上这个知识库提问。
第 4 步才是真正压测环境的一步:文档解析走 worker,分段索引写向量库,检索时读向量库。这一步过了,说明向量库、worker、文件存储三处都没问题。第 3 步过了但第 4 步卡住,问题基本锁定在向量库或 worker 上。
5. 避坑记录:五个把我按倒在地的报错
这一段是整篇里最像血泪经验的部分。下面五条都是我在不同机器上真实撞过的,现象、原因、解决按顺序写,撞上了直接对号入座。
5.1 向量库没起来,api 一直重连
现象:docker compose ps里只有 db、redis、api、web、nginx、sandbox,没有 weaviate;docker compose logs api里反复刷连接weaviate:8080被拒。
原因:新版 compose 文件用 profiles 管理向量库,不带--profile启动时这些服务不会被创建。.env里的VECTOR_STORE只是告诉 api 该连谁,不负责把容器拉起来。
解决:先docker compose config --services确认服务名,再用docker compose --profile weaviate up -d启动。已经起了一半的不用down,直接带 profile 再up -d就会把缺的补上。
5.2 80 端口被占,nginx 容器起不来
现象:docker compose up -d报Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use,nginx 容器状态是Created而不是Up。
原因:宿主机上已有服务监听 80,云服务器镜像预装 nginx 是最常见的情况。
解决:先sudo ss -lntp | grep :80找到占用进程,确认它没用就systemctl stop掉;不能停的话,改.env的EXPOSE_NGINX_PORT=8080,然后docker compose up -d nginx重建这一个容器。注意老版本 compose 文件可能没暴露这个变量,那就得直接改docker-compose.yaml里 nginx 的 ports 映射,改之前先备份。
5.3 SECRET_KEY 改完不生效
现象:.env里明明换成了新生成的密钥,登录之后刷新页面就掉线,或者接口报 401。
原因:docker compose restart只重启进程,不重新读取.env。环境变量是在容器创建时注入的,变量变了必须重建容器。
解决:
# 只重建受影响的三个服务,别整个 down 掉 docker compose up -d --force-recreate api worker web同时提醒:SECRET_KEY一旦有用户在用就不能随便换,换了等于所有人会话失效。要在上线前定好。
5.4 内存不够,worker 被 OOM kill
现象:worker容器状态在Restarting之间反复跳,docker inspect <容器名> --format '{{.State.ExitCode}}'输出 137;dmesg -T | grep -i 'killed process'能看到对应记录。
原因:4GB 内存的机器上同时跑 api、worker、向量库、PostgreSQL、Redis,一旦做文档索引,内存峰值直接越界。
解决:短期加 swap 顶一下;长期升到 8GB。另外可以调小并发,.env里有 worker 数量相关的参数(不同版本名字略有差异,以.env.example里的注释为准),把数量从默认值降下来能明显压低峰值,代价是索引变慢。
5.5 down -v 之后数据没了
现象:执行过一次docker compose down -v,再up -d进去发现应用、知识库、账号全空,只剩初始化页面。
原因:-v是删命名卷的意思,数据库和向量库的数据都在卷里。这个参数的本意是「彻底重置」,敲之前必须清楚后果。
解决:只能从备份恢复。所以先养成备份习惯:
cd /opt/dify/docker # 把 .env 里的变量读进当前 shell,避免把库名用户名写死 set -a; source .env; set +a # 导出数据库;建议加进 crontab 每天跑一次 docker compose exec -T db pg_dump -U "$DB_USERNAME" "$DB_DATABASE" \ > backup_$(date +%F).sql向量库的数据要单独备份,富余做法是定期把整个docker/volumes/打包。上传的文件也在这个目录下,别只备份数据库。
6. 装完怎么验证:从健康检查到一次端到端调用
装完不等于装好。我见过容器全绿、页面能开,但一上传文件就 500 的环境——容器状态只能说明进程活着,说明不了链路通。所以最后这一步我会固定跑一套验证,五项,缺一项都不算完。
| 验证项 | 命令或操作 | 期望结果 |
|---|---|---|
| 容器状态 | docker compose ps | 所有容器Up,有健康检查的是healthy |
| 编排语法 | docker compose config -q | 无任何输出,表示配置可解析 |
| 后端接口 | curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/console/api/setup | 返回 200 |
| 沙箱连通 | docker compose exec api curl -s http://sandbox:8194/health | 返回健康响应 |
| 端到端 | 建应用 → 配模型 → 对话 → 传文档建知识库并检索 | 四步全部成功 |
第三项和第四项最容易被跳过,但它们恰好覆盖了两个最容易坏的地方。具体命令:
cd /opt/dify/docker # 1) 编排文件语法与变量替换结果,静默即通过 docker compose config -q # 2) 后端是否响应;200 说明 nginx 到 api 这一段通了 curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/console/api/setup # 3) 沙箱内部连通性,代码执行节点依赖它 docker compose exec api curl -s http://sandbox:8194/health # 4) 看最近 200 行日志里有没有 ERROR 级别记录(正常应该很少) docker compose logs --tail=200 | grep -iE 'error|traceback' | head第 2 项如果返回 502,问题在 nginx 到 api 这一段,去看docker compose logs nginx;返回 000 说明连不上,先确认端口对不对。第 3 项失败的话,应用里任何涉及代码执行的节点都会报错,但聊天功能照常,很容易被忽略,直到某天用上工作流才炸。
端到端那一步,我建议加一个容易漏的检查:上传一个中文文件名的文档,看知识库里的文件名是否正常。文件名编码问题在部分系统的 locale 配置下会出现,症状是文件上传成功但列表里显示乱码,或者点下载 404。发现这类问题,先查.env里的FILES_URL有没有填,再查宿主机的 locale。
从那以后我每次起新环境,都强制先跑docker compose config -q加健康检查三连,最后完整走一遍「传文档 → 建知识库 → 检索问答」,一项不过就不往下走。这套流程花不了五分钟,但能省掉后面排查半天的时间。希望帮到你。
本文还有配套的精品资源,点击获取