1. 升级前的整体判断:1.8.1 到 1.9.2,到底动了什么
先说结论:Dify 这个项目用 Docker Compose 部署起来确实轻松,一条 docker compose up -d 就能跑,但升级迁移从来不是简单拉个镜像就完事。1.8.1 到 1.9.2 这个跨度,中间隔了多个小版本,虽然官方的 Release Notes 会把每个版本的改动列得清清楚楚,但如果你的环境用了外部 PostgreSQL 和 Weaviate,升级时真正要操心的不是功能差异,而是三块硬骨头:数据库表结构变更、向量库索引兼容性、以及 .env 里那些肉眼看不见但丢了就要命的密钥。
先给正在观望的人一个明确建议:如果在生产环境跑着 Dify,升级前一定要把这一篇读完整再动手。如果是本地测试环境,直接 docker compose pull && docker compose up -d 问题也不大,但依然建议走一遍正规流程,因为只有把 SOP 养成了习惯,生产环境出问题的时候才不会手忙脚乱。
从我实际接触过的升级案例来看,Dify 1.9.x 相对于 1.8.x,改了前端构建方式、知识库分段逻辑、部分 API 响应结构,同时新增了一些配置项。这些东西在界面上看可能差异不大,但底层很可能涉及 PostgreSQL 表结构变动和 Weaviate 索引元数据格式调整。换句话说,只要官方版本号跨了 minor 版本,就不能默认数据完全兼容。
升级前读一遍官方 Release Notes,重点看三个地方:
- Breaking Changes:有没有破坏性变更,比如环境变量改名、某个服务拆分。
- Database Migration:有没有附带的数据库迁移说明。
- Weaviate 版本要求:
docker-compose.yaml里 Weaviate 镜像 tag 是否发生了变化。
这里有个很关键但容易被忽略的点:Dify 官方 compose 文件在 1.9.2 中可能调整了 Weaviate 的镜像版本,如果你的向量库里已经有大量知识库文档,Weaviate 主版本或小版本跨得太大,索引格式不一定兼容。所以升级前一定要把你当前用的 compose 文件和官方最新版做一次 diff,看清楚向量库和数据库镜像 tag 到底变没变。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 升级前的准备动作:把家底摸清楚
2.1 确认当前部署形态
很多人连自己部署的 Dify 是哪种形态都没搞清楚就动手升级,这是大忌。Dify 的部署方式至少有三种:纯 Docker Compose、Docker Compose + 外部 PostgreSQL/Weaviate、Kubernetes Helm。本文的 SOP 针对的是第二种,也就是标题里写的 PostgreSQL + Weaviate + Docker Compose 这种最主流的自建方式。
动手前先确认三件事:
- PostgreSQL 是容器里的还是独立部署的?独立部署的话,版本是多少?Dify 官方默认要求 PostgreSQL 12+。
- Weaviate 是容器里的还是外部服务?当前镜像 tag 是多少?
- 目前 Dify 的版本到底是不是 1.8.1?有时候你以为是 1.8.1,实际容器里跑的镜像 tag 是 latest,已经悄悄变了好几个版本了。
查看当前版本最简单的方式是登录 Dify 界面,看平台设置里的版本号,或者直接看 API 容器的镜像 tag:
bash复制docker ps --filter "name=api" --format "{{.Image}}"
如果输出的是 langgenius/dify-api:1.8.1,那就说明当前确实是 1.8.1。如果输出的是 latest,那就比较麻烦了,因为 latest 会随时漂移,你得先用 docker inspect 看镜像的创建时间或标签历史,确定实际版本。
2.2 梳理自定义配置
这一步是升级翻车的重灾区。Dify 官方提供的 docker-compose.yaml 和 .env.example 只是默认配置,绝大多数人都会做一定程度的自定义。常见的自定义点包括:
- 端口映射:默认 80/443,有人改成 8080 或其他端口。
- 外部数据库连接:把
db服务替换成外部 PostgreSQL,DB_HOST指向局域网或云数据库。 - 对象存储:把默认的本地存储换成 S3、OSS、MinIO。
- 模型供应商凭证:
.env里配置的 OPENAI_API_KEY、自定义模型 endpoint 等。 - Nginx 配置:有人会改 Nginx 的 client_max_body_size、开启 HTTPS。
这些自定义配置在升级时极容易丢。我的习惯是每次升级前,先把自己当前的 docker-compose.yaml 和 .env 都另存一份,再和官方最新版做 diff,把官方新增或修改的配置项合并到自己的文件里。
2.3 建立一个升级窗口
除非是测试环境,否则别在业务高峰期做升级。Dify 升级过程中需要停服务、备份数据库,整个过程预计 20 到 40 分钟。建议把升级窗口选在晚上或者业务低峰期,并且提前通知使用平台的同事,避免升级到一半有人正在跑工作流,出现数据不一致。
3. 数据备份:升级前不做备份,等于裸奔
3.1 PostgreSQL 逻辑备份
PostgreSQL 的备份方式很多,升级前最稳妥的方式是做一个逻辑备份(pg_dump),因为逻辑备份是纯 SQL 文件,与你当前的 PostgreSQL 版本和 Dify 版本绑定程度最低,回滚的时候最容易恢复。
如果 PostgreSQL 跑在容器里,先找到容器名:
bash复制docker compose ps | grep postgres
假设容器名是 docker-db-1(不同项目的容器名前缀不一样,以实际输出为准),备份命令如下:
bash复制docker exec docker-db-1 pg_dump -U postgres -d dify -F c -f /tmp/dify_backup_$(date +%Y%m%d_%H%M%S).dump
docker cp docker-db-1:/tmp/dify_backup_20250601_120000.dump /data/backup/
如果你的 PostgreSQL 是外部独立部署的,直接在本机执行:
bash复制pg_dump -h <DB_HOST> -U <DB_USER> -d <DB_NAME> -F c -f dify_backup.dump
这里有两个容易踩的坑:
- 不要在容器还在运行时直接
docker cp整个 PostgreSQL 数据目录。虽然 PostgreSQL 有 WAL 机制,但直接拷贝数据目录不能保证一致性,恢复的时候容易出问题。 - 备份文件的权限:
pg_dump是用容器内 postgres 用户执行的,备份文件默认属于 postgres 用户,docker cp到宿主机后你可能需要chmod 644才能正常读取。
补充一个问题:psql 报 “无法创建锁文件”
很多人在宿主机上装了一个 psql 客户端,然后直接执行 psql -U postgres 连容器里的数据库,结果报错:
code复制无法创建锁文件 "/var/run/postgresql/.s.pgsql.5432.lock": 权限不够
这个报错的原因是 psql 默认尝试通过 Unix socket 连接本地 PostgreSQL,而 /var/run/postgresql 目录当前用户没有写权限。解决办法很简单,显式指定 TCP 连接:
bash复制psql -h 127.0.0.1 -p 5432 -U postgres -d dify
或者设置环境变量 PGHOST=127.0.0.1。
3.2 Weaviate 数据备份
Weaviate 是向量数据库,备份逻辑和关系型数据库完全不同。如果只是升级 Dify 而 Weaviate 镜像 tag 没变,那理论上数据不会动,但为了保险起见,我还是建议做一次备份。
Weaviate 官方推荐用 Backup API,但这个 API 在默认配置下不一定可用,需要配置备份后端。最简单的办法是直接备份数据卷。
先查看 Weaviate 容器挂载的数据卷:
bash复制docker inspect docker-weaviate-1 --format '{{range .Mounts}}{{.Name}} -> {{.Destination}}{{end}}'
假设数据卷名字是 docker_weaviate_data,先停掉 Weaviate 容器再打包:
bash复制docker compose stop weaviate
docker run --rm -v docker_weaviate_data:/data -v /data/backup:/backup alpine tar czf /backup/weaviate_data_$(date +%Y%m%d).tar.gz -C /data .
docker compose start weaviate
注意:停 Weaviate 容器会导致依赖向量库的功能暂时不可用,所以这个操作要放在升级窗口内。如果嫌麻烦,也可以在 Weaviate 运行的时候直接 tar,但这样备份的数据不能保证 100% 一致,只在紧急情况下用。
3.3 环境配置备份与镜像留档
数据库备份完之后,还有两个容易遗漏的备份项:
.env文件:这个是 Dify 的配置核心,至少拷贝一份到安全目录。- 当前镜像版本号:记录下当前所有 Dify 相关镜像的 tag,方便回滚时使用。
bash复制docker compose images > images_before_upgrade.txt
如果空间允许,最好把当前版本的镜像也打个 tag 留档:
bash复制docker tag langgenius/dify-api:1.8.1 langgenius/dify-api:rollback-1.8.1
docker tag langgenius/dify-worker:1.8.1 langgenius/dify-worker:rollback-1.8.1
这样即使以后官方把 1.8.1 的镜像从 Docker Hub 下架(这种事发生过),你本地还是有回滚的凭据。
4. 正式升级:从 compose 文件到容器替换
4.1 获取新版本 compose 文件并做差异合并
Dify 的官方 compose 文件在 GitHub 仓库的 docker/docker-compose.yaml 目录下,1.9.2 的版本需要去对应 tag 下下载,不要直接用 latest 分支,因为 master 分支可能已经超前了。
下载后和当前的 compose 文件做对比:
bash复制diff docker-compose.yaml.bak docker-compose.yaml.new
diff 结果重点看这几类变化:
- 镜像 tag 变化:
api、worker、web的镜像版本号。 - 新增/删除的环境变量:比如某个服务新增了环境变量,需要同步到
.env。 - 挂载目录变化:比如
web容器新增了 volume,或者nginx的配置路径调整。 - 服务依赖变化:
depends_on条件可能调整了,影响启动顺序。
这里我要强调一个经验:不要直接把自己的自定义 compose 文件和官方新版的混在一起用,而是基于官方新版重新做自定义。也就是说,先用官方新版文件跑,等确认 Dify 核心功能正常了,再把你的自定义项(比如端口、存储、域名)手动加回去。
原因很简单:官方 compose 文件之间的差异如果交织在一起,你很难判断某个问题到底是版本升级导致的,还是自定义配置冲突导致的。分步走,问题定位会快很多。
4.2 更新 .env 配置
官方仓库里同时提供了 .env.example,把新版的 .env.example 下载下来,和你现有的 .env 做对比。
升级 1.9.2 时我踩过的一个实际问题是:新版引入了 DB_PASSWORD 之外的一些新配置项,如果不加进去,容器启动后某些功能会异常。所以最好的做法是,以新版 .env.example 为模板,把原 .env 里的值逐项同步过去,不要反过来。
有一个例外:SECRET_KEY 必须保留旧值。Dify 用 SECRET_KEY 对模型供应商的密钥等敏感信息做加密,升级后如果 SECRET_KEY 变了,你会发现所有已配置的模型密钥全部失效,页面上提示解密失败。这种问题只能通过工具一个个重新配置,非常痛苦。
4.3 拉取新镜像并启动
配置合并完成、备份文件确认无误后,可以开始正式操作。
如果当前服务还在运行,先停掉:
bash复制docker compose down
注意:docker compose down 默认会删除容器和网络,但不会删除数据卷,所以数据还是安全的。如果你在 compose 文件里定义了自定义 volume,只要不带 -v 参数,数据卷就不会被删。
然后拉取新镜像:
bash复制docker compose pull
拉取完成后启动:
bash复制docker compose up -d
这里说明一个新手容易困惑的点:docker compose up -d 和 docker compose start 的区别。up -d 会检查配置变化,重新创建配置有变动的容器;start 只是启动已经存在的容器,配置不会重新加载。所以升级必须用 up -d,否则新镜像不会生效。
启动后不要急着结束,先看日志:
bash复制docker compose logs -f db-migrate
Dify 的 compose 文件中通常定义了一个 db-migrate 服务,负责在启动时执行数据库迁移。如果这个服务报错,说明数据库 schema 迁移有问题,需要马上看具体错误信息。如果 db-migrate 正常退出,再继续看 api 和 worker 的日志:
bash复制docker compose logs -f api
docker compose logs -f worker
5. 数据层专项处理:PostgreSQL 和 Weaviate 的升级要点
5.1 PostgreSQL 侧要检查什么
如果你的 PostgreSQL 是 Dify 内置的容器,升级 Dify 时通常不会动数据库镜像本身,但如果 compose 文件里 PostgreSQL 镜像 tag 变了,就要格外小心。
检查 PostgreSQL 容器的日志,确认没有权限类错误:
bash复制docker compose logs db | tail -n 50
常见的 PostgreSQL 启动问题包括:
- 共享内存不足:报错
could not resize shared memory segment,需要在docker-compose.yaml里给 db 服务加shm_size配置,比如shm_size: 1gb。 - 表空间权限问题:如果数据卷从老版本迁移过来,目录所有者变了,导致 PostgreSQL 无法写入,报错
Permission denied。 - 版本差异过大:PostgreSQL 从 12 直接跳到 16,需要
pg_upgrade或转储恢复,不能直接升级数据目录。
如果升级后 Dify 的 api 容器一直报数据库连接错误,先用最朴素的方式验证数据库本身是健康的:
bash复制docker exec docker-db-1 pg_isready -U postgres
输出 accepting connections 说明数据库没问题,问题大概率出在 Dify 的 .env 配置里。
5.2 Weaviate 侧要注意版本兼容
Weaviate 是 Dify 的默认向量数据库选择之一,1.9.2 版本对 Weaviate 的要求可能在 compose 文件里体现为镜像版本调整。
如果 Weaviate 镜像 tag 没变,升级完 Dify 后只需要确认 Weaviate 容器正常运行,且 Dify 的日志里没有连接 Weaviate 的错误即可。
如果 Weaviate 镜像 tag 变了,那么要特别注意一个现象:Weaviate 升级后,旧索引数据不一定能自动兼容。Weaviate 官方对索引格式兼容性是有明确说明的,跨大版本升级时,有时需要重建索引,或者做一次全量备份恢复。
实际操作中,我发现一个降低风险的办法:升级 Dify 时保持 Weaviate 镜像 tag 不变。也就是说,先只升级 Dify 的 api、worker、web 等组件,向量库维持原状。等 Dify 新版本稳定运行一段时间,再单独评估要不要升级 Weaviate。这样可以把出问题的范围控制在最小。
如果一定要升级 Weaviate,建议走这条流程:
- 按前文步骤备份 Weaviate 数据卷。
- 停止整个 Dify 栈:
docker compose down。 - 修改 compose 文件中的 Weaviate 镜像版本。
- 先单独启动 Weaviate:
docker compose up -d weaviate。 - 确认 Weaviate 健康检查通过:
docker compose logs weaviate | tail -n 30。 - 再启动整个栈:
docker compose up -d。
如果 Weaviate 启动失败,立即检查数据卷是否可以挂载,必要时回滚镜像版本。
6. 升级后的功能验证:用清单说话
Dify 升级完成后,验证不是随便打开页面看一眼就完事,我习惯按下面的清单逐项核验,确保没有遗漏。
6.1 前端可用性验证
打开 Dify 的 Web 界面,确认页面能正常加载,版本号显示为 1.9.2。如果页面白屏、样式错乱,多半是浏览器缓存了旧版静态资源,强制刷新(Ctrl+Shift+R)或清除缓存后再试。
如果还有问题,查看 web 容器日志:
bash复制docker compose logs web | tail -n 50
1.9.x 的前端构建方式和旧版有差异,如果日志里出现静态资源 404 之类的错误,检查 nginx 的配置是否和官方新版一致。
6.2 数据完整性验证
这一步比前端可用性更重要,因为 Dify 升级导致的数据丢丢失往往不会立刻暴露,而是过几天才被发现。
- 进入知识库列表,逐个打开关键知识库,确认文档状态正常,没有全部变成“错误”或“待处理”。
- 在知识库里执行一次检索,看召回结果是否正常,确认向量检索链路没断。
- 跑一遍现有的工作流,尤其是依赖知识库、依赖模型调用的工作流,确认各节点正常。
- 查看已配置的模型供应商凭证,确认没有出现“解密失败”的提示。
- 用现有的 API Key 调一次应用接口,确认 API 通道正常。
6.3 日志与资源监控
升级后的前 30 分钟是观察窗口,持续查看 api 和 worker 的日志,确认没有反复出现的 Error 或 Exception:
bash复制docker compose logs --since 30m api | grep -i error
docker compose logs --since 30m worker | grep -i error
同时看下系统资源使用情况。Dify 升级后如果出现 api 容器反复重启,很可能是内存不足导致的 OOM,用以下命令确认:
bash复制docker stats
如果 api 容器内存经常逼近上限,考虑调整 .env 里的相关配置,或者在宿主机上增加内存。别指望 Dify 默认配置能扛住高并发,1.9.x 的 worker 并发模型和老版本有差异,需要根据自己的数据量和调用频率调整。
6.4 常见问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| compose stop 时卡住,报 exit status 1 | 容器没有响应 SIGTERM,健康检查卡死 | 先 docker compose stop -t 60 增加超时,不行就 docker kill <容器名> |
| 页面提示模型密钥解密失败 | SECRET_KEY 与升级前不一致 | 用旧 .env 里的 SECRET_KEY 替换,重启 api 容器 |
| 知识库文档全部报错 | Weaviate 连接异常或索引不兼容 | 查看 weaviate 日志,确认接口地址是否正确,必要时重建索引 |
| 数据库无法连接 | PostgreSQL 容器未启动或密码错误 | 检查 docker compose ps,核对 .env 里的 DB_* 配置 |
| 前端白屏 | 浏览器缓存了旧版静态资源 | 强制刷新,或检查 web/nginx 容器日志 |
| API 响应变慢 | 数据库索引失效或 worker 并发不足 | 查看 PostgreSQL 慢查询日志,调整 worker 配置 |
| 上传文件大小受限 | Nginx 配置未同步新版本 | 检查 nginx 的 client_max_body_size |
7. 回滚方案:升级失败时怎么撤
聊回滚之前先纠正一个观念:回滚不是羞耻的事,能把服务恢复到可用状态比硬撑着一个坏版本重要得多。而且回滚方案应该在升级前就写好,不要等到出了问题再想。
7.1 回滚的前提条件
想顺利回滚,你必须满足以下条件:
- 备份文件完整:PostgreSQL 备份、Weaviate 数据卷备份、
.env文件备份。 - 旧版本 compose 文件保留。升级前我建议把旧 compose 文件重命名保存,比如
docker-compose.yaml.bak-1.8.1。 - 旧镜像可用。如果本地没有旧镜像,优先在升级前
docker pull langgenius/dify-api:1.8.1,否则回滚时会发现 Docker Hub 上已经没有这个 tag 了。
7.2 回滚操作步骤
在升级失败需要回滚的情况下,按照下面的顺序操作:
- 停止当前服务:
docker compose down。 - 用旧的
docker-compose.yaml和.env覆盖当前版本,注意先备份一份当前的新配置,避免回滚完成后想再查新版本配置时找不到。 - 恢复 PostgreSQL:清空当前数据库,导入备份文件。
- 恢复 Weaviate 数据卷:如果 Weaviate 数据没有变化,可以直接用旧镜像启动;如果数据卷动过,需要先删除现有数据卷再重新挂载备份的 tar 包。
- 启动服务:
docker compose up -d。 - 验证:登录界面,确认版本号为旧版本,知识库和工作流正常。
整个过程大概 30 到 60 分钟,取决于数据库大小。这里特别提醒一句:Dify 升级后,即使用户在界面上没有做任何操作,后台也可能写入了新的表或数据。如果你在升级后的版本里已经创建了新应用、跑过工作流,那么回滚后这些数据会丢失。所以回滚是最后手段,能修复就优先修复,回滚应当是不得已的选择。
7.3 回滚后的注意事项
回滚完成后,不要认为万事大吉。还需要做三件事:
- 查看 api 和 worker 日志,确认没有持续报错。
- 确认数据库连接和 Weaviate 连接正常,特别是跨版本升级后再回滚,向量索引可能已经被新版本格式写坏,需要重建。
- 告知相关用户回滚完成,避免用户在旧版本上继续操作新版本产生的数据。
8. 升级过程中的几个关键经验和心得
这次从 1.8.1 升到 1.9.2,我自己觉得最有价值的经验有三条。
第一,升级 Dify 的核心不是版本差异,而是数据一致性。只要 PostgreSQL 有完整备份、Weaviate 数据卷有备份、SECRET_KEY 没变,大多数问题都只是时间问题。反过来,这三个里任何一个出了问题,轻则某些功能不可用,重则知识库数据全丢。
第二,官方 compose 文件的差异合并要有节奏。不要偷懒直接把旧文件里所有自定义项搬到新文件,有冲突时宁可先使用官方默认配置,确认核心功能跑通后再逐步恢复自定义项。这样排查问题时会非常省力。
第三,保持旧镜像和旧配置文件至少保留一个版本周期。比如这次升级完 1.9.2,我会把 1.8.1 的镜像和配置继续保留两周。两周内如果发现 1.9.2 有严重问题,随时可以回滚;两周后确认稳定了,再清理旧镜像。
最后再分享一个小技巧:升级时把每一步操作的命令和输出都记录到一个日志文件里,比如 upgrade_20250601.log。这样万一出了问题,你能清楚地知道哪一步开始异常的,排查起来比凭空回忆高效得多。Dify 的升级流程虽然比直接 docker compose pull 繁琐,但整个操作下来,只要准备充分,真正停服的时间通常可以控制在十几分钟以内,这对一个承载知识库和工作流的系统来说,完全是值得的。
