简介:这是一份从 GitHub 获取的 Dify 官方安装包,面向希望本地部署、源码阅读或二次开发 Dify 的开发者与运维人员,可解决官方项目文件获取、环境搭建与功能扩展的入门需求。压缩包共收录 2000 个文件,约 20.25MB,以 1337 个 Python 源码文件为主体,辅以 366 个 JSON 配置、116 个 CSS 与 84 个 JavaScript 前端资源,另含 Markdown 文档、YAML 编排文件及少量 Shell、HTML、SQL 等,基本覆盖后端服务、前端界面、配置说明与部署脚本等模块,目录组织相对完整。已有 2982 人学习下载,说明该安装包在部署实践与源码研究场景中具有一定参考热度。解压后可获得项目主干代码、依赖配置与说明文档,便于读者梳理 Dify 的目录结构、理解服务启动与前后端协作流程,并在此基础上完成本地运行、接口调试或按需裁剪功能,适合作为部署实践与源码阅读的基础材料。
1. 从 GitHub 拿到 dify 官方安装包:先分清发布包和源码包
搜「dify 官方安装包」的人,十有八九是打开 GitHub 仓库首页点绿色 Code 按钮,下载 ZIP 解压,然后docker compose up -d一把梭。报错随后就到:.env不存在、volumes目录是空的、compose 里的变量全是空值。翻车原因不是命令敲错,是动手前没分清要的是哪一份东西。仓库首页给的是源码主干,docker/目录虽然也在,但缺了发布流程生成的模板和初始化资源;Releases 里挂的才是打包好的部署包,.env.example、middleware.env.example、nginx配置、ssrf_proxy一并齐全。
这个差别对只想在内网跑一套 LLM 应用编排平台的人很关键:你要的是能直接 compose 起来的部署包,不是参与开发的源码树。下面按「下哪个包、包里有什么、怎么改、怎么起、哪里会卡」拆一遍,适合第一次部署这类平台的后端和运维,也适合已经跑起来、想把它从单机玩具推进内网正式环境的熟手。
2. 拆开安装包看目录:六个核心服务与三份必改的配置
2.1 docker 目录的分层与 volumes 的归宿
解压之后先别急着起服务,花三分钟把目录过一遍,后面排查能省半小时。与部署相关的文件全部集中在docker/下面,仓库根目录是源码和文档,部署时基本不碰。
# 进入发布包解压目录,只看 docker 这一层 cd dify-*/docker ls -1 # 常见内容:docker-compose.yaml docker-compose-template.yaml # .env.example middleware.env.example # nginx/ ssrf_proxy/ volumes/ certs/这段命令的作用是把部署平面和代码平面切开看。docker-compose.yaml是主编排文件,docker-compose-template.yaml是上游维护用的模板,改配置不要动模板,一律用 override 文件叠加。.env.example是运行时环境变量模板,middleware.env.example是中间件(数据库、缓存)的密码与库名模板,两者分工不同,很多人只复制了第一个,结果数据库密码和 compose 里引用的变量对不上,容器起来就退出。
volumes/是整份部署里最不能丢的目录。它挂着应用生成的文件、数据库数据文件、缓存快照、插件包和沙箱临时产物。默认 compose 用 bind mount 把宿主机路径映射进去,所以备份策略可以很简单——停服务、打包整个volumes/、再起服务,而不是去容器里docker cp。我一般会在.env里把文件存储路径显式指向一个独立磁盘挂载点,别放在系统盘,日志和用户上传的附件涨得比想象中快。
ssrf_proxy/和nginx/是两个容易被忽略但会影响功能可用性的目录。前者是出站请求的代理层,用来防止应用被诱导去访问内网地址;后者是入口反向代理,负责把控制台、Web 应用 API、文件下载三条路径分流转发。certs/放自签证书,只有在你打算直接在这台机器上终结 HTTPS 时才需要动它,正规做法是前面再挂一层网关。
2.2 服务清单:谁先起、谁等谁、谁可以不要
一份完整的 compose 文件里会拉起七八个容器,但它们的重要性完全不同。把下面这张表对着docker-compose.yaml的services:段过一遍,心里就有谱了。
| 服务 | 作用 | 是否必须 | 关键依赖 |
|---|---|---|---|
api | 后端主服务,处理控制台请求与业务逻辑 | 必须 | 数据库、Redis、向量库 |
worker | 异步任务进程,跑索引构建、批量处理 | 必须 | 数据库、Redis |
web | 前端控制台静态资源与 SSR | 必须 | api |
db | PostgreSQL,存业务元数据与工作流定义 | 必须(可外置) | 无 |
redis | 缓存与 Celery 消息队列 | 必须(可外置) | 无 |
weaviate | 默认向量库,存知识库切片向量 | 视场景(可替换) | 无 |
sandbox | 代码执行沙箱,跑工作流里的代码节点 | 用到代码节点就必须 | 无 |
plugin_daemon | 插件运行时,承载模型供应商等插件 | 必须 | api、数据库 |
nginx | 入口反向代理与静态文件 | 建议保留 | api、web |
ssrf_proxy | 出站请求代理 | 建议保留 | 无 |
依赖关系上有两个硬顺序。第一是数据库必须在api之前可用,api启动时会跑迁移,连不上库会直接退出,容器策略如果是on-failure就会陷入重启循环,日志里反复刷连接拒绝。第二是plugin_daemon需要api的内网地址可达,它启动时会去注册,api没起来它就一直重试。因此 compose 里常见depends_on只保证启动顺序,不保证服务就绪,真正稳妥的做法是在api前面加健康检查,或用外部脚本轮询端口。
sandbox是可选服务的典型。如果你只是搭个聊天应用、跑跑提示词编排,完全可以把这段注释掉,省一个容器。但只要工作流里出现代码执行节点,没有它就会在节点执行时报超时或连接失败,而且错误信息不会直接告诉你「沙箱没起」,只会在任务日志里丢一个堆栈。这个坑我踩过一次,排查方向一度跑偏到网络策略上。
2.3 三份必须动过的配置与镜像版本锁定
默认配置能跑,但绝对不能直接上生产。至少要动三处。
第一处是.env里的SECRET_KEY。它参与会话令牌和敏感字段的加解密,默认值是公开的模板值。生成方式:
# 生成一个足够长的随机密钥,替换 .env 中的 SECRET_KEY openssl rand -base64 42这个值一旦定下来就不能随便改。改了之后旧的登录态全部失效,更麻烦的是数据库里用旧密钥加密的敏感字段(比如模型供应商的 API Key)会解不开,控制台里表现为供应商配置读取失败。所以第一次部署就把它定死,写进配置管理,别等跑了两周再改。
第二处是数据库与缓存密码。middleware.env.example复制成middleware.env,把POSTGRES_PASSWORD、REDIS_PASSWORD换成强口令,同时确保.env里的DB_PASSWORD、REDIS_PASSWORD与之完全一致。这两份文件是分开引用的,密码不一致的表现是api容器日志里 PostgreSQL 报password authentication failed,但db容器本身是健康的,很容易误判成网络问题。
第三处是镜像版本。.env里有一组控制各服务镜像 tag 的变量,常见的是DIFY_API_VERSION、DIFY_WEB_VERSION、DIFY_SANDBOX_VERSION这类命名(具体字段以你手上那份.env.example为准,不同发布批次会有增删)。默认常常是latest,这在测试环境省事,在正式环境是定时炸弹——某天上游推了新镜像,你重启一次容器就跨了三四个版本,数据库迁移可能直接失败。我一般会把这几个变量统一钉到同一个发布 tag 上,前端、后端、沙箱版本保持一致,升级时整体替换,不单独升某一个。
# 锁定版本:把 .env 中所有镜像 tag 变量改成同一个发布版本 # 注意:不要只改 api 不改 web,前后端版本错配会导致接口字段对不上 grep -nE '^DIFY_.*_VERSION=' .env这段是查而不是改,先看清楚有哪些版本变量存在,再统一替换。改完之后docker compose config看一眼渲染结果,确认变量都拿到了值、没有出现空字符串——空值会让镜像地址变成repo/:这种非法形式,报错信息很难读。
3. Compose 起服务:从 .env 改到能登录控制台
3.1 最小改动集与配置分层
部署这件事最怕一上来就全量定制,改到一半自己都记不清动了哪些。我的做法是先做一份「最小可运行配置」,能登录控制台、能创建应用、能跑通一次对话,再往上叠外部依赖。这样出问题时至少有个已知可用的基线。
# 第一步:复制模板,建立最小配置 cd docker cp .env.example .env cp middleware.env.example middleware.env # 第二步:生成密钥并写入 .env SECRET=$(openssl rand -base64 42) sed -i "s|^SECRET_KEY=.*|SECRET_KEY=${SECRET}|" .env # 第三步:确认关键字段存在且非空 grep -E '^(SECRET_KEY|DB_USERNAME|DB_PASSWORD|DB_DATABASE|REDIS_PASSWORD|CONSOLE_API_URL|APP_WEB_URL)=' .envsed那行直接替换而不是追加,是为了避免模板里本来就有SECRET_KEY=空行,追加后会形成两行同名变量,后一行覆盖前一行,实际生效的还是空值。这种问题在.env解析里很常见,排查时用grep看有没有重复键比看日志快。
CONSOLE_API_URL和APP_WEB_URL是最容易被忽略、又最容易在换域名时出问题的两个变量。前者是控制台调后端 API 的基地址,后者是 Web 应用对外访问的基地址。本机测试可以留空或写http://localhost,一旦上了内网域名就必须写全,否则前端请求会打到错误地址,表现是页面能开、登录按钮点了没反应、浏览器控制台一片 CORS 报错。
配置分层比一把改.env更好维护。官方给的 compose 文件不要直接改,新建一个docker-compose.override.yaml,把端口、卷路径、资源限制这些环境相关的东西写进去。升级时只需替换主 compose 文件,override 原样保留,改动一目了然。
# docker-compose.override.yaml:只放环境相关差异,不覆盖服务定义 services: api: environment: # 关掉生产环境不需要的调试日志级别,避免日志量爆盘 LOG_LEVEL: INFO # 迁移开关:首次启动必须为 true,之后可视情况保留 MIGRATION_ENABLED: "true" nginx: ports: # 宿主机端口冲突时改这里,别去改主 compose - "8080:80"override 文件的合并规则是「后覆盖前、映射深合并」。这意味着你写ports是追加而不是替换,如果主文件里 80 端口已经映射了,两边会同时存在,docker compose up直接报端口占用。遇到这种情况,正确做法是用!reset标记或者干脆在主文件里把该端口段注释掉。
3.2 起服务与观测启动过程
# 后台拉起全部服务 docker compose up -d # 看容器状态,重点看 STATUS 是不是 Up 而不是 Restarting docker compose ps # 跟随 api 日志,首次启动会跑数据库迁移,能看到建表语句 docker compose logs -f --tail=200 apidocker compose ps里的Restarting是最有价值的一个信号。它意味着容器起来了又退,通常是配置错误或依赖不可达。这时候别急着重启,先docker compose logs --tail=100 <服务名>看退出原因,日志第一屏往往就有答案。
首次启动api会执行数据库迁移,这段时间日志会持续输出,几十秒到两三分钟不等,取决于机器性能。迁移期间前端页面打不开是正常的,不要在这个时候反复重启容器——迁移脚本没有幂等保护的部分被打断,可能需要手工清理半成品表结构,这是真血泪经验。
观测顺序建议固定下来:先看db和redis是否 Up,再看api是否完成迁移并进入监听状态,然后看plugin_daemon是否注册成功,最后看nginx。按依赖顺序看,能一次定位到链条上第一个断掉的环节,而不是在一堆报错里找因果。
3.3 管理员初始化与插件运行时验证
服务起来之后,第一件事是设置管理员账号。发布包常见做法是在.env里通过INIT_PASSWORD预设初始密码,首次启动时创建管理员;也有版本要求在控制台首次访问时走引导页创建。两种都行,关键是别把默认密码留在环境里太久。
# 验证后端已就绪:健康检查接口返回正常状态 curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1/console/api/health # 验证插件守护进程已注册:查 api 日志中的插件连接记录 docker compose logs api | grep -i plugin | tail -20第一个命令只看状态码,200 说明后端路由和数据库都通了。返回 502 说明api还没起或者nginx转发配置有问题;返回 500 大概率是数据库连接或迁移未完成。
第二个命令是很多人会漏掉的一步。模型供应商、工具、扩展能力现在全部以插件形式挂载,plugin_daemon没跑通,控制台里模型列表就是空的,你会以为是要去配置什么密钥,实则是运行时没起来。日志里能看到守护进程注册的连接信息和版本,如果反复出现重连,多半是api的内网地址在容器网络里解析不到,检查 compose 里的服务名与网络别名。
插件本身是「软件/插件」这条线上最容易被低估的部分。离线内网环境没法从在线市场拉取,需要用本地安装的方式上传离线包,路径一般在控制台的插件管理页面。上传前先确认守护进程版本与插件的兼容声明一致,版本不匹配时安装会静默失败,只在守护进程日志里留一行错误。
4. 把数据库和向量库拆出去:连接参数与迁移顺序
4.1 什么时候必须外置中间件
单机 compose 自带的 PostgreSQL、Redis、向量库适合演示和小流量试用,但有三种情况必须拆出去。
第一种是多实例部署。你要跑两个api副本做负载,两个副本必须连同一个数据库和同一个 Redis,否则会话、任务队列、缓存全部错乱。第二种是数据量上来之后。知识库切片向量在几万条以内,容器内向量库还能扛;到了几十万条,内存和磁盘 IO 会把整台机器拖垮,而且备份窗口越长风险越大。第三种是运维规范要求。数据库要独立做备份、做从库、做审计,塞在应用宿主机上的容器里,这些都没法做。
拆的顺序建议先拆数据库,再拆 Redis,最后动向量库。数据库拆完验证一轮完整业务流程,再动下一个。一次性全拆,出问题时不知道是哪个连接串写错了。
4.2 连接参数逐项拆解
Dify 的数据库配置走一组DB_*变量,外置时把它们指向独立实例,同时把 compose 里的db服务整段注释掉,避免白白占端口和内存。
# 外置 PostgreSQL:修改 .env 中这组变量 DB_HOST=10.0.0.21 DB_PORT=5432 DB_USERNAME=dify DB_PASSWORD=换成强口令 DB_DATABASE=dify # 连接池相关,按实例规格调,别照抄默认值 SQLALCHEMY_POOL_SIZE=30 SQLALCHEMY_POOL_RECYCLE=3600DB_HOST不要写localhost。容器里的localhost指的是容器自己,这是新手最常见的错误,表现是连接被拒绝但宿主机上psql明明能连上。要么写宿主机的内网 IP,要么在 compose 里用host.docker.internal配合extra_hosts。
SQLALCHEMY_POOL_RECYCLE这个参数值得单独说。很多托管数据库或中间的连接网关会主动断开空闲超过一定时长的连接,应用侧连接池不知道,拿到的是一条已被对端关闭的连接,于是偶发地报「server closed the connection unexpectedly」。把回收时间设得比网关的空闲超时短,比如网关 1 小时断,这里就设 3600 秒以内,能把这个玄学问题压下去。
Redis 同理,REDIS_HOST、REDIS_PORT、REDIS_PASSWORD、REDIS_DB四个字段,外置时注意库号隔离,别和其他业务共用同一个 db index,键冲突排查起来非常痛苦。另外CELERY_BROKER_URL如果单独配置了,它和REDIS_*是两套入口,改一处不改另一处,会出现页面正常但异步任务永远排队的情况。
4.3 切换向量库的重建与数据迁移
向量库是这套系统里替换成本最高的一环。.env里的VECTOR_STORE决定用哪家,常见取值覆盖内置的 weaviate、外置的 qdrant、milvus、pgvector 等。切换时有三件事必须一起做,缺一件就是白忙。
第一,改VECTOR_STORE和对应的连接配置,然后重启api和worker。只重启api不行,索引构建是worker干的,它还在用旧配置。
第二,重建知识库索引。切换向量库不会自动搬数据,已有的知识库在新的向量库里是空的。控制台里对每个知识库执行重新索引,或者用接口批量触发。
# 切库后确认 worker 已加载新配置 docker compose exec worker env | grep -i vector # 观察索引任务日志,确认写入的是新库 docker compose logs -f --tail=100 worker第一条命令直接看进程环境变量,比翻配置文件快,也能避免「改了 .env 但容器没重建」这种假象——docker compose restart不会重新读取.env,必须up -d才会重建容器。
第三,旧库先别删。新库索引跑完、抽几个知识库做召回验证、确认命中率正常之后再下线旧库。向量化模型如果也换了,新旧向量空间不可比,召回结果会明显变差,这时候要全量重跑而不是增量补。
5. 启动卡住的五种情况:避坑排查清单
5.1 密钥、配置与版本类
现象:改过SECRET_KEY之后,控制台能登录但模型供应商配置全部报错,或提示解密失败。原因是用旧密钥加密的敏感字段在新密钥下解不开,数据库里存的是密文,不是明文。解决办法是提前规划密钥生命周期,第一次部署就固定下来;确实要轮换,得先把已存的供应商密钥在控制台里重新录入一遍(或清空后重配),再切新密钥,顺序反了就只能回滚旧密钥救场。
现象:api容器反复重启,日志末尾是数据库连接失败或迁移异常。多数情况不是数据库的问题,而是.env和middleware.env里的密码不一致,或者DB_HOST写成了localhost。排查顺序是先用docker compose exec db psql -U <用户> -d <库名>手工验证账密,再用docker compose exec api env | grep DB_确认容器内实际拿到的值,两边对齐基本就能定位。
现象:升级后前端页面空白,接口 404 或字段解析报错。前后端镜像版本不一致。web和api是配套发布的,接口契约会变,只升一半必然出问题。解决办法是把.env里所有镜像 tag 变量统一改成同一个发布版本,然后docker compose up -d整体重建,不要用restart。
5.2 网络、架构与存储类
现象:调用内网自建模型服务一直超时,但从宿主机curl是通的。出站请求被ssrf_proxy拦截了。这个代理层的设计目的是防止应用被诱导访问内网地址,副作用是把你自己的内网模型地址也拦了。解决办法是在代理配置里把目标网段加入白名单,或者为该模型供应商单独指定不走代理。改完记得重建api和worker两个容器。
现象:在 ARM 架构机器上sandbox容器起不来,一直处于退出状态。镜像没有对应架构的构建产物。要么换用支持该架构的镜像源,要么把代码执行能力指向一个独立的执行端点(通过代码执行相关的端点与密钥变量配置),把沙箱从这套 compose 里摘出去。硬扛着让它反复重启没有意义,日志里只会给你一个 exec format error。
现象:上传文件失败,或知识库文档处理卡在解析中。先看volumes/所在磁盘是不是满了,再看目录属主。容器内进程的 UID 和宿主机目录属主不匹配时,写入会失败但错误被上层吞掉,只在api日志里留一行权限拒绝。用df -h和ls -ln volumes/两条命令就能确认,把属主调成容器运行用户即可,别图省事直接chmod 777。
6. 上线前的自检与升级演练:一次跑完的检查脚本
部署完成不等于可用。我习惯在交付前跑一遍固定检查,把「能打开页面」和「功能真的通」区分开。下面这个脚本覆盖容器状态、后端健康、入口转发、插件守护四层,任何一层失败都会立刻打印出来。
#!/usr/bin/env bash # deploy-check.sh:部署后自检,参数为入口地址 set -uo pipefail BASE="${1:-http://127.0.0.1}" FAIL=0 # 1. 容器状态:任何非 Up 都算失败 for svc in api worker web db redis plugin_daemon nginx; do state=$(docker compose ps --format '{{.Service}} {{.State}}' 2>/dev/null | grep "^${svc} " | awk '{print $2}') if [ "$state" != "running" ]; then echo "[FAIL] 容器 ${svc} 状态为 ${state:-未创建}" FAIL=1 fi done # 2. 后端健康检查:只看状态码,不解析 body code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "${BASE}/console/api/health") [ "$code" = "200" ] || { echo "[FAIL] 后端健康检查返回 ${code}"; FAIL=1; } # 3. 前端入口:能拿到 HTML 才算转发通了 curl -s --max-time 5 "${BASE}/" | grep -qi '<html' || { echo "[FAIL] 前端首页未返回 HTML"; FAIL=1; } # 4. 插件守护:日志里应能查到注册记录 docker compose logs api 2>/dev/null | grep -qi 'plugin' || { echo "[WARN] 未见插件守护注册记录"; } [ "$FAIL" -eq 0 ] && echo "[OK] 四层检查全部通过" || exit 1脚本里刻意没有去解析 JSON 响应内容,只判断状态码和容器状态,原因是健康检查的返回结构在不同发布批次之间有差异,写死字段解析会在升级后集体误报。第三项判断<html是为了确认nginx到web的转发链路完整,这个链路断了的表现就是白屏加 502,光看后端健康是发现不了的。
升级演练比自检更重要。我的固定动作是:先在测试机用同一份.env拉新版本跑通,确认迁移日志无异常;然后备份整个volumes/目录和数据库逻辑备份;接着在正式环境把镜像 tag 整体替换、docker compose up -d重建;最后重跑上面这个脚本,并抽查一个已有知识库的召回和一个带代码节点的工作流。缺任何一步我都不太放心,尤其是数据库逻辑备份,容器卷备份不能替代它——卷里的数据文件跨大版本恢复是有兼容风险的。
回滚路径也要提前想清楚:镜像 tag 改回旧值、up -d重建即可,但如果新版本已经执行了不可逆的数据库迁移,回滚会失败。所以在升级前一定要确认发布说明里有没有标注破坏性变更,有的话就先把数据库备份单独落到另一台机器上,这是唯一有效的后悔药。
从那以后我每次交付这类平台,不管多急,都强制走一遍「备份 → 升级 → 自检 → 抽查业务」这四步,宁可多花二十分钟,也不想半夜起来处理一个本可以在下午三点发现的问题。希望帮到你。
本文还有配套的精品资源,点击获取