
简介这是一份适配国内下载场景的 Dify-main 项目归档面向需要快速部署 Dify 应用、或受 Docker Hub 网络访问限制影响的开发与运维人员。压缩包共 2000 个文件大小 20.29MB以 1411 个 Python 文件、370 个 JSON、103 个 CSS 为主另含 JS、YAML、Shell 等辅助文件覆盖后端逻辑、前端样式、配置模板与运维脚本基本保留 Dify 主分支的目录结构便于直接还原或对照学习。对于想离线部署 LLM 应用平台的读者配合国内 Docker 镜像源可显著降低拉取成本并减少公网依赖同时也可作为源码阅读、二次开发或理解 Dify 配置项的参考样例。目前已有 1481 人学习/下载适合有一定 Docker 或 Python 基础的中级开发者快速入手。1. 国内镜像版本 dify-main先搞清楚你拉到的到底是什么如果你在配置一台国内服务器准备部署 Dify 来做私有化的 LLM 应用大概率会遇到一个非常尴尬的场面docker compose up -d输出去之后界面卡在 Pulling 阶段几分钟都不动最后直接timeout。这时候你就会意识到一件事——部署 dify-main 这种主分支镜像网络环境是个绕不过去的前置条件。标题里的“国内可以”说的不是 Dify 本身要墙内墙外而是它的 Docker 镜像能不能在国内网络环境下顺利拉取、能不能找到可靠的镜像版本来源。dify-main 指的是 Dify 主分支代码构建的镜像对应的 tag 通常是main它比 release 版本更新、功能更激进但也意味着你要直面镜像拉取、环境变量配置和容器编排的一堆细节。这篇文章就把这套东西完整讲清楚先讲怎么让 Docker 拉镜像不卡再讲怎么把 dify-main 跑起来、怎么升级回滚最后把常见翻车点列全。适合所有打算在自己服务器上私有化部署 Dify、又想跟进 main 分支最新功能的人。2. 先把 Docker 的镜像拉取速度治好加速器、daemon.json 与拉取命令2.1 为什么拉 dify-main 总是卡住先理解镜像分发的基本逻辑Docker 默认从 Docker Hub 拉取镜像而 Docker Hub 的 CDN 节点在国内的连通性并不稳定尤其是几个百 MB 级别的大镜像。dify-main 相关的主要是langgenius/dify-api、langgenius/dify-web这两个仓库下的maintag 镜像再加上nginx、postgres、redis、weaviate、sandbox等依赖镜像一口气拉下来可能接近 2-3 GB。任何一个镜像卡住整个docker compose up就会一直阻塞。解决的核心思路是在 Docker 配置里加上镜像加速器。所谓镜像加速器本质是一个 Docker Hub 的代理缓存服务国内有多家云厂商在运营这些加速节点。配置好之后docker pull会先从加速器拉取如果节点上有缓存速度会明显提升如果节点没有缓存它会回源到 Docker Hub 再转给你。这里要先说清楚你配置的是 Docker 镜像加速器它只负责加速Docker 官方仓库的镜像下载不涉及任何其他类型的网络请求。常见的加速器地址包括阿里云容器镜像服务、腾讯云、中科大、网易等。每家提供的加速地址格式不同但配置方式在 Linux 上都是大同小异。2.2 Linux 服务器配置 daemon.json三条路径和校验方法我一般会先到 /etc/docker/ 目录下检查 daemon.json 是否存在。如果不存在直接新建一个。下面是一份比较完整的配置示例里面加了多个镜像加速器作为备选同时把>sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.mirrors.ustc.edu.cn ], data-root: /var/lib/docker } EOF sudo systemctl daemon-reload sudo systemctl restart docker这段配置做了三件事第一registry-mirrors里声明了三个镜像加速地址Docker 在拉取镜像时会按顺序尝试第二>docker info | grep -A 5 Registry Mirrors docker pull langgenius/dify-api:maindocker info输出的Registry Mirrors字段会列出你配置的地址。然后再手动拉取一次langgenius/dify-api:main如果能在一分钟左右看到下载进度条滚动起来说明加速器配置有效。如果还是卡住需要检查服务器防火墙是否放行了 Docker 需要访问的域名和端口。2.3 Windows / macOS 桌面版的配置路径不碰配置文件的替代方案如果你是在本地电脑上用 Docker Desktop 做开发调试不建议直接去改 daemon.json。Docker Desktop 提供了图形化的镜像加速配置入口路径是 Settings → Docker Engine里面显示的其实就是当前生效的 JSON 配置你可以把加速器地址加到registry-mirrors数组里。修改后 Docker Desktop 会自动重启不需要命令行操作。这里有个很常见的误解很多人以为在 Docker Desktop 里配置了加速器就能全局生效但实际只有 Docker Desktop 自己的引擎会读这份配置。如果你同时在 WSL2 里安装了原生的 docker-ce两边配置是独立的WSL2 里面的 Docker daemon 仍然要按 2.2 节的 Linux 方式来配置。当前几年 WSL2 成为主流开发环境之后这个坑几乎每天都能在社群里看到有人踩。2.4 本地拉取与镜像版本选择main 标签和 release 标签的差异加速器配置好之后接下来要决定拉哪个 tag。dify 的 Docker Hub 仓库里langgenius/dify-api和langgenius/dify-web一般会同时存在多个标签标签类型适用场景稳定性main想要最新功能、愿意频繁升级功能最新但可能存在未充分验证的问题latest跟随默认的发布节奏相对稳定通常对应最近的发布版本1.x.x等版本号生产环境固定版本最稳定回滚可控如果你只是想在本地体验 dify-main 的最新功能拉maintag 就好。但如果你要把这套镜像部署到生产环境服务真实用户我的建议是第一套环境拉maintag 做验证确认功能符合预期后再切换到稳定 tag 上线。用docker pull langgenius/dify-api:main和docker pull langgenius/dify-web:main把两个核心镜像先手动拉下来后面docker compose up就不会再卡在拉取阶段了。3. 用 dify-main 镜像把 Dify 完整跑起来源码获取、环境变量与容器编排3.1 获取 dify-main 源码从国内可直接访问的代码仓库拉取镜像只是 Dify 运行时的容器化封装真正把整套系统编排起来的还需要 docker-compose 配置、环境变量模板和 nginx 路由配置这些都在源码仓库里。所以部署 dify-main 的实际步骤是先拿到含 docker 编排目录的主分支源码再在上面配置和启动。由于 GitHub 在国内的连通性受网络环境影响较大我常用的做法是从国内可访问的代码平台拉取 dify 仓库。很多开发者会把 Dify 主分支同步到 Gitee 这类国内代码托管平台上在平台上搜索 dify-main找到一个更新频繁的仓库同步源然后直接git clone下来比从 GitHub 拉取稳定很多。git clone https://gitee.com/your-mirror/dify.git dify-main cd dify-main git checkout main git log -1 --oneline第二行进入源码目录第三行切换到 main 主分支。git log -1 --oneline是为了确认你当前所在的提交点这个提交号要记下来——它决定了你的代码和镜像是否处于同一版本。如果你拉的镜像maintag 构建于某个特定提交而源码 checkout 到另一个提交运行时会因为接口契约不一致出现各种奇怪报错。3.2 环境变量初始化改掉默认密钥补上你必须知道的关键配置Dify 源码根目录下有一个.env.example文件里面列出了全部可配置项。部署前要把它复制成.env再逐项调整否则所有默认密钥都相同存在明显的安全隐患。cp .env.example .env然后打开.env至少要修改下面这几个配置项。第一项是SECRET_KEY它用于加密会话数据必须改成一个足够长的随机字符串可以用openssl rand -base64 42生成。第二项是POSTGRES_PASSWORD默认密码是弱口令数据库端口如果暴露到公网被扫到就是灾难。第三项是INIT_PASSWORD这是管理员初始密码只在首次初始化时使用初始化完成后建议去后台改掉。还有几个容易被忽略的非安全配置项。UPLOAD_FILE_SIZE_LIMIT默认是 15MB部分版本是 10MB如果你要上传大文件给模型做知识库处理这里必须调大。LOG_LEVEL默认是 INFO开发调试时可以改成 DEBUG但生产环境保持 INFO 就行调试级日志会把所有请求参数打出来。以下是一个常用裁剪后的核心配置参考# 生成随机密钥并写入 .envmacOS/Linux 通用 echo SECRET_KEY$(openssl rand -base64 42) .env # 使用 sed 原地替换数据库密码 sed -i s/POSTGRES_PASSWORD.*/POSTGRES_PASSWORDyour_strong_password/ .env # 查看修改后的关键配置 grep -E SECRET_KEY|POSTGRES_PASSWORD|INIT_PASSWORD|LOG_LEVEL .env第一行生成随机的 SECRET_KEY 追加到 .env 文件末尾。如果 .env 里已经有SECRET_KEY这个条目追加方式会生成重复键后读取的配置会覆盖前值所以更稳妥的办法是用 sed 替换而不是追加。第二行的 sed 只做精确替换把 POSTGRES_PASSWORD 这一行的值改成你设置的强密码。第三行是核对确保这几个配置没有缺失。注意如果 POSTGRES_PASSWORD 里包含、/、\这类特殊字符sed 的替换可能触发语法错误。常见的处理方式有两种一是直接用手工编辑器修改 .env二是先改用不包含特殊字符的密码初始化完成之后再通过 SQL 命令修改数据库密码。3.3 调整 docker-compose 编排把本地 build 换成纯镜像启动现在打开docker/docker-compose.yaml看一眼各服务的定义。Dify 的编排文件默认同时写了image和build两个字段build指向本地 Dockerfile。如果你没改任何配置直接执行docker compose up -dDocker 会尝试用本地源码重新构建镜像整个过程会拉取大量基础依赖耗时极长。既然你在用 dify-main 的镜像版本正确的做法是把build字段注释掉强制使用image字段指定的远程镜像。API 服务、Web 服务、Sandbox 这三个常用服务都做了同样的调整cd docker vi docker-compose.yaml在api、web、sandbox三个服务下把各自的build和context、dockerfile配置项整体注释掉保留image字段。注释完之后api服务的配置会变成类似这样api: image: langgenius/dify-api:main # build: # context: . # dockerfile: Dockerfile restart: always environment: MODE: api注释掉build之后容器启动时会直接从 Docker Hub 拉取langgenius/dify-api:main不会再看本地源码。如果你在 2.4 节已经手动拉过镜像这一步几乎不会有延迟。要注意的是docker-compose.yaml里还有worker服务它和api用同一个镜像、但环境变量MODE不同worker也要同样注释掉build。3.4 启动与首次验证compose up、健康检查与日志定位编排文件调整完成后执行启动命令docker compose up -d docker compose ps docker compose logs -f api第一条命令进入后台启动模式-d表示 detached日志输出不会占住终端。第二条命令查看所有服务的运行状态重点关注 STATE 列是否都为 Up以及 HEALTHCHECK 是否显示 healthy。第三条命令跟踪 api 容器的日志输出看到类似Application startup complete或端口监听日志说明 API 服务已经就绪。首次启动后不要急着登录后台先等 30 到 60 秒让数据库初始化完成。直接访问 http://服务器IP/ 地址时正常情况会跳到初始化页面让你设置管理员账号。如果页面一直白屏或者返回 502大概率是 nginx 容器启动时 api 容器还没就绪可以再等一会儿后刷新。如果一直不恢复执行docker compose logs nginx和docker compose logs api对比看是路由问题还是后端起不来。4. 镜像升级与 dify-main 版本管理从 main 分支到稳定发布的切换与回滚4.1 先确认当前差了多少个版本查镜像层数不如看 git 提交号很多人在维护 dify-main 时会有一个习惯每隔几天就跑一次docker compose pull把镜像更新到最新看到新镜像拉取完成就觉得升级成功了。但实际上镜像更新不等于应用升级。Dify 在主分支上频繁调整数据库表结构、API 接口参数和前端构建逻辑镜像拉完还要检查源码是否同步、数据库是否需要迁移、配置是否兼容。我维护 dify-main 时养成的习惯是升级前先做两个检查。第一用git log -1 --oneline看源码当前提交第二用docker inspect查看镜像元数据里记录的源码提交地址和构建时间。如果源码提交时间早于镜像构建时间说明源码比镜像旧必须先把源码拉新git fetch origin main git log HEAD..origin/main --oneline git diff HEAD..origin/main --stat | tail -5第一行从远程拉取 main 主分支的更新但不会动本地工作区。第二行列出本地与远程 main 分支之间差多少个提交第三行显示涉及哪些文件有变更。这一步的价值在于提前判断升级风险如果变更集中在api/core和docker/这两个目录说明有数据库或部署结构变动升级时要重点验证如果只有前端web/的改动升级就相对安全。4.2 标准的升级流程源码、镜像、容器、数据库四步走确认差异后执行下面的升级序列。这套流程我在多个环境里验证过基本能保证可用性git pull origin main cd docker docker compose pull api web worker docker compose up -d --remove-orphans docker compose exec api flask db upgrade docker compose restart api worker第一步git pull把源码更新到与镜像同期的提交。第二步docker compose pull拉取新镜像。第三步up -d --remove-orphans会重建配置有变更的容器并清理掉不再需要的孤儿容器比如版本升级后取消了某个服务旧的容器实例会被自动移除。第四步执行数据库迁移这一步最关键因为 Dify 每次升级都可能在 PostgreSQL 里增加字段或新建表数据库结构没有迁移新版本 API 启动会直接报错。第四步还有一种情况如果你执行flask db upgrade时报错提示没有迁移脚本通常是因为源码还没有更新到与镜像匹配的提交或者容器里挂载的代码卷还是旧代码路径。先回 4.1 节做一次差异检查再重新执行。4.3 回滚方案docker tag 留后手数据库迁移前备份升级翻车了怎么办很多人的第一反应是重新拉回旧 tag。这个做法对 release 版本有效但对maintag 不适用——因为 main tag 指向的是动态更新的分支镜像你无法从 Docker Hub 拉回“某个时间点的旧 main”。所以维护 dify-main 必须自己留后手。常见的做法是升级前给当前镜像打一个本地标签作为回滚点。比如当前测试环境运行正常升级前用下面的命令把正在运行的镜像固定下来docker tag langgenius/dify-api:main langgenius/dify-api:backup-20250601 docker tag langgenius/dify-web:main langgenius/dify-web:backup-20250601 docker compose exec db pg_dump -U postgres dify backup-20250601.sql第一、二行把正在使用的镜像打上带日期的 tag这个 tag 只存在本地不会同步到 Docker Hub。第三行通过 pg_dump 导出整个 Dify 数据库。回滚时的操作就是把 compose 文件里的image临时改回这个 backup tag再恢复数据库。要注意的是镜像回滚到旧版本后数据库如果已经是新版本结构读取时可能遇到不兼容所以备份数据库这一步必须做 pg_dump 导出的 SQL 是最后一道后悔药。5. 国内部署 dify-main 的避坑记录拉取失败、启动崩溃与数据初始化5.1 拉取已配置加速器仍然超时现象是 pull 进度条不动最后报 network timeout原因有两层一是某些加速器节点在国内不同地区、不同运营商的连通性差异极大比如北方某运营商到某加速器节点的路由经常拥塞但换一个节点就正常二是 docker pull 会同时开启多个并行下载层如果其中一个层卡死整个拉取任务就会挂着。解决方法是先把加速器列表里的地址精简到一个表现最好的节点不要同时堆五六个地址。然后手动拉单个镜像测试定位是哪个镜像卡住。如果某天dify-api已经拉完、dify-web卡住可以考虑直接从代码平台推送的镜像仓库拉取——部分国内代码托管平台的容器镜像仓库对特定用户做了同步加速这可以单独配置一个私有加速地址。5.2 启动时提示version is obsolete或者 Compose 配置解析失败现象是 docker compose up 还没开始拉镜像就中断原因通常是服务器上安装的 docker-compose 插件版本太老无法识别编排文件里某些较新的字段。Dify 的 compose 文件在不同版本间迁移时配置格式也会随之变化我遇到过旧版 compose 插件不认pull_policy字段的情况。解决方法是把 docker-compose 升级到较新的版本。这里要区分两种情况使用docker compose插件的升级 Docker 本身即可使用独立二进制docker-compose的要下载新版本覆盖旧文件。升级后用docker compose version确认版本号再重新执行启动。5.3 api 容器反复退出日志里报 PostgreSQL 权限错误现象是容器启动几秒后退出日志中出现permission denied for schema public原因通常是挂载的数据库卷里面的文件归属不对或者首次初始化时数据库目录权限不足。有一种典型场景是 data 卷之前用 root 用户初始化过后来换成非 root 方式运行容器PostgreSQL 进程访问不了旧目录。解决方法是备份数据库卷内容后重建卷并重新初始化。如果数据库里还没有重要数据直接把挂载目录删掉让容器重建最省事如果已有重要数据用docker compose stop db停掉数据库再在宿主机上执行chown -R调整目录归属然后重启。调整归属前可以把卷目录复制一份到 /tmp 留作备份。5.4 Web 界面能打开但登录后报错 500api 日志显示getaddrinfo ENOTFOUND现象是前端页面正常渲染但请求后端 API 时提示服务异常原因通常是容器间通过服务名互访时api 容器解析不到 db 或 redis 的主机名。可能是自定义网络没接好也可能是你手动指定过container_name导致 DNS 记录冲突。解决方法比较简单检查docker network ls里 Dify 项目的网络是否正常连接了 api、db、redis 三个容器。如果网络异常docker compose down后重新docker compose up -d让编排文件重建网络。Dify 默认使用 docker-compose 自动创建的项目网络不要去手工改名或改 IP。5.5 生产环境上传文件频繁失败提示文件大小超限现象是知识库上传大文件时直接被前端拦截后端没有日志原因有两层nginx 容器的 client_max_body_size 限制是一个层面Dify 后端的 UPLOAD_FILE_SIZE_LIMIT 是另一个层面。只改后端nginx 会替你先拦截掉大请求只改 nginx后端又会报负载过大。两个都要改。后端改.env里的UPLOAD_FILE_SIZE_LIMIT例如改成50单位 MB。前端 nginx 的限制在docker/nginx/conf.d/default.conf里找到client_max_body_size那一行改大。改完重启docker compose restart nginx api worker sandbox不要只重启 api因为上传链路会经过 nginx 转发、api 处理、sandbox 做文件解析环节上任何一个没改都可能继续翻车。5.6 API 容器一直显示 unhealthy但日志又看不到 fatal 错误现象是 HTTP 请求可以正常处理但健康检查一直失败原因一般是健康检查命令本身依赖某个工具或某个内部端点。Dify 的 api 健康检查走的是内部端口健康检查命令在容器里执行时依赖 shell 环境。如果镜像被改造过、包管理器精简过健康检查就会一直失败。这种情况虽然不影响业务流量但会导致 compose 的依赖启动顺序判定异常比如 web 容器会一直等到 api healthy 才启动。解决方法是修改 compose 编排文件里 api 服务的 healthcheck 配置把检查命令换成更通用的 curl 请求路径或者直接去掉 healthcheck 依赖让 web 容器在 api 端口可达时就启动。6. 收尾技巧给 dify-main 镜像做一次体检和一键可移植的备份部署完成后我习惯用一组固定命令做完整体检避免等用户发现问题时才去排查。这套检查分成三层进程层、接口层、数据层。进程层看所有容器是否都是 Up 且 healthy接口层直接用 curl 打到 API 的健康检查端点数据层检查 PostgreSQL 里核心表是否能正常读写。一整套命令如下docker compose ps --format table {{.Name}}\t{{.Status}} curl -s http://127.0.0.1/ | grep -o title.*/title docker compose exec api python -c from flask import current_app with current_app.app_context(): print(db ok) 第一行以表格形式输出容器状态新手容易漏看 STATUS 列里的 health 状态。第二行只检查 nginx 首页是否能正常返回这个响应快但不能证明后端功能正常。第三行是进入 api 容器内执行一个最小化的数据库连通性检查如果这条命令能打印db ok说明前后端和数据库这条主链路是通的——这条是最可靠的健康信号比单纯看容器状态有用得多。备份方面Dify 的数据由 PostgreSQL、Redis 缓存和上传文件三部分组成。Redis 是缓存可以丢但 PostgreSQL 和上传文件不能丢。我用的是基于 Docker 卷的打包方案先把容器停掉以保证数据一致性然后打包整个项目卷目录最后传到对象存储或另一台备份机。恢复时在新服务器上把 Dify 启动起来在数据库容器还没初始化完成的窗口期把卷覆盖回去再重新docker compose up -d。docker compose stop api worker docker run --rm -v dify-main_docker_volumes:/data -v /backup:/backup alpine \ tar czf /backup/dify-full-$(date %Y%m%d).tar.gz /data docker compose start api worker这里用了一个临时 alpine 容器来打包-v dify-main_docker_volumes挂载的是 compose 项目创建的卷-v /backup把宿主机备份目录映射进去。容器内执行 tar 把整个 /data 打成一个带日期的压缩包打完即退出。注意卷名前面要带有 compose 项目名前缀如果不知道卷名用docker volume ls | grep dify查一下。最后说一个我自己的习惯每次做完升级或者大变更我不会清理老的 backup tag 镜像而是保留最新的两个备份点。原因是 main 分支的迭代速度太快有时候你回滚到“上一次的镜像”发现它已经和当前数据库结构不兼容了还要再往前找一个点。多留一个备份点就多一次尝试机会。这套方法不复杂但能让你在面对 dify-main 的快速迭代时从容不少希望帮到你。本文还有配套的精品资源点击获取