Dify Linux部署:容器化交付与系统级环境预检指南

📅 2026/7/16 15:36:51 👁️ 阅读次数 📝 编程学习
Dify Linux部署:容器化交付与系统级环境预检指南

1. 为什么Dify在Linux上部署不是“装完就跑”,而是要重新理解容器化交付逻辑

你搜“Dify Linux部署”,页面里全是“三步搞定”“一键安装”的标题党,点进去却发现:有人卡在Docker没装对,有人docker compose up报错说找不到配置文件,还有人明明服务起来了,但浏览器打不开8000端口——最后翻了二十页GitHub Issues才搞懂,原来默认配置里WEB_CONCURRENCY设成了0,根本没启动Web进程。

这不是操作手册写得差,而是绝大多数教程把Dify当成了传统软件在安装,而它本质是一个由多个异构服务协同工作的云原生应用系统:前端React静态资源、后端FastAPI API、向量数据库Qdrant、异步任务队列Celery+Redis、模型推理网关(可选Ollama或OpenAI兼容接口),全靠Docker Compose编排联动。你在Ubuntu上敲下docker compose up -d的那一刻,实际是在启动一个微型Kubernetes集群的简化版。

我去年帮三个客户做Dify本地化部署,最短耗时2小时(客户已熟练使用Docker),最长一次花了17小时——不是因为命令不会输,而是因为他们在CentOS 7上硬扛glibc 2.17,而Dify官方镜像要求glibc ≥2.28,最后发现连docker compose二进制本身都因GLIBC版本不兼容直接Segmentation Fault。这件事让我彻底放弃“照着文档走就行”的幻想,转而建立了一套Linux环境健康度预检清单,现在所有部署前必跑一遍:

# 检查内核版本(Dify依赖cgroup v2) uname -r # 检查glibc版本(关键!Qdrant和部分Python扩展强依赖) ldd --version # 检查Docker Engine是否启用cgroup v2(Ubuntu 22.04+默认开启,但老系统需手动改grub) cat /proc/1/cgroup | head -n1 # 检查Docker Compose版本(v2.20+才完整支持profiles和healthcheck语法) docker compose version # 检查可用内存(Qdrant单实例建议≥4GB,否则向量索引构建失败静默退出) free -h

提示:很多“部署失败”问题根本不在Dify代码里,而在Linux发行版底层。比如Debian 11的docker.io包自带的是Docker Compose v1,而Dify的docker-compose.yml明确用了profiles字段——这是v2才有的特性。你用docker-compose命令去跑,必然报错no configuration file provided: not found,因为v1根本不认识这个字段。这不是你配置写错了,是工具链断层了。

所以本篇不叫“Dify安装教程”,而叫“Linux系统部署Dify”,重点就在“系统”二字。我们要做的不是复制粘贴几行命令,而是让Linux这台精密机器,准备好承载Dify所有服务模块运行的土壤:内核能力、用户权限、存储路径、网络策略、资源隔离边界,全部要提前对齐。后面每一步操作,背后都有对应的操作系统级原理支撑,而不是“别人这么写,我就这么敲”。

2. Docker与Docker Compose:别再混淆它们的职责边界,否则你会反复重装系统

很多人部署失败的第一道坎,其实是分不清Docker Engine和Docker Compose到底谁管什么。网上教程动不动就写“先安装Docker,再安装Docker Compose”,听起来像装两个独立软件,但其实它们的关系更接近“操作系统内核”和“系统初始化脚本”——Docker Engine是真正干活的引擎,Docker Compose只是个YAML解析器+API调用器。

2.1 Docker Engine:Linux容器运行时的“地基”

Docker Engine不是普通应用,它是以dockerd守护进程形式常驻内存的系统服务,直接调用Linux内核的namespaces(隔离PID、网络、挂载点等)和cgroups(限制CPU、内存、IO)。当你执行docker run hello-world,实际流程是:

  1. dockerCLI向dockerd发送HTTP请求;
  2. dockerd创建新命名空间,挂载rootfs,设置cgroup限制;
  3. dockerd调用runc(OCI运行时)启动容器进程;
  4. 进程在隔离环境中运行,输出日志回传给CLI。

这意味着:Docker Engine的安装必须由root权限完成,且其配置直接影响所有容器的安全基线。比如默认情况下,Docker守护进程监听unix:///var/run/docker.sock,任何能访问该socket的用户(如加入docker组的用户),等价于拥有root权限——因为你可以启动一个挂载了宿主机根目录的容器,直接读写任意文件。

注意:绝不要在生产环境将普通用户加入docker组!正确做法是用sudo docker或配置细粒度的dockerdTLS认证。我在某次客户部署中发现运维小哥为图方便把开发组全员加进docker组,结果有实习生误删了/var/lib/docker,整个Dify知识库向量索引全毁,恢复花了9小时。

2.2 Docker Compose:声明式编排的“指挥官”,不是安装包管理器

Docker Compose v2(注意是v2,不是v1)本质是个Go写的二进制程序,它不管理容器生命周期,只做三件事:

  • 解析docker-compose.yml,校验语法和字段合法性;
  • 将每个service转换为docker run参数,调用Docker Engine API创建容器;
  • 监听容器状态,提供up/down/logs等聚合命令。

关键认知:Docker Compose本身不包含任何容器镜像,也不下载镜像。它只是告诉Docker Engine:“请按这个配置拉取并运行这些镜像”。所以当你看到docker compose up报错pull access denied,问题一定出在:

  • 镜像名拼写错误(如difyai/dify:main写成difyai/dify:master);
  • 私有镜像仓库未登录(docker login registry.example.com);
  • 阿里云镜像加速器配置失效(国内用户常见)。

我实测过不同安装方式对后续维护的影响,结论很明确:必须用Docker官方APT源安装Engine,用curl下载二进制安装Compose v2。原因如下:

安装方式Docker Engine来源Docker Compose来源后续升级风险兼容性问题
Ubuntuapt install docker.ioDebian社区包(旧版)自带v1(已废弃)升级需手动清理残留profiles/healthcheck语法不识别
阿里云镜像站apt install docker-ceDocker官方CE版需单独安装v2apt upgrade可能覆盖v2为v1极少,但需确认docker compose version
推荐:官方脚本一键装Docker官方CE最新版curl -L https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-linux-x86_64手动替换二进制,完全可控无,v2.20+全面兼容Dify 0.6+

执行官方安装脚本的命令(适配Ubuntu/Debian/CentOS):

# 卸载旧版(如有) sudo apt-get remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg lsb-release # 添加Docker官方GPG密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 添加稳定版仓库 echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 安装Docker Engine sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

实操心得:最后一行docker-compose-plugin是关键!它把Compose v2作为Docker CLI插件安装,执行docker compose up而非docker-compose up,避免PATH冲突。很多教程让你ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose,结果which docker-compose指向错误路径,调试时绕晕三天。

3. Dify官方部署包解剖:从docker-compose.yml看懂服务依赖拓扑

Dify官方GitHub仓库的docker/deploy目录下,那个看似简单的docker-compose.yml,其实是整套系统的“DNA图谱”。它定义了7个服务之间的通信协议、数据流向、启动顺序和健康检查逻辑。不理解这个文件,你就永远在“黑盒重启”——服务挂了就docker compose down && up -d,却不知道哪个环节断了。

我们逐段拆解Dify 0.6.3版本的docker-compose.yml核心结构(已去除注释和非关键字段):

services: # 前端服务:纯静态资源,Nginx反向代理 nginx: image: nginx:alpine ports: ["80:80", "443:443"] volumes: ["./nginx.conf:/etc/nginx/nginx.conf:ro"] depends_on: - api - web # 后端API服务:FastAPI + SQLAlchemy api: image: difyai/dify:main command: ["uvicorn", "app.main:app", "--host", "0.0.0.0:8000", "--port", "8000"] environment: - DATABASE_URL=postgresql://postgres:postgres@db:5432/dify - REDIS_URL=redis://redis:6379/0 - CELERY_BROKER_URL=redis://redis:6379/1 depends_on: - db - redis - worker # 异步工作节点:处理知识库切片、模型调用等耗时任务 worker: image: difyai/dify:main command: ["celery", "-A", "app.worker.celery_worker:celery", "worker", "--loglevel=info"] environment: - DATABASE_URL=postgresql://postgres:postgres@db:5432/dify - REDIS_URL=redis://redis:6379/0 - CELERY_BROKER_URL=redis://redis:6379/1 depends_on: - db - redis # PostgreSQL数据库:存储用户、应用、会话等结构化数据 db: image: postgres:15-alpine environment: - POSTGRES_DB=dify - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres volumes: ["./volumes/postgres:/var/lib/postgresql/data"] # Redis缓存:会话存储、任务队列、临时数据 redis: image: redis:7-alpine command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] volumes: ["./volumes/redis:/data"] # Qdrant向量数据库:知识库语义检索核心 qdrant: image: qdrant/qdrant:1.7.4 environment: - QDRANT__SERVICE__HOST=0.0.0.0 - QDRANT__SERVICE__PORT=6333 - QDRANT__STORAGE__PATH=/qdrant/storage volumes: ["./volumes/qdrant:/qdrant/storage"] healthcheck: test: ["CMD", "curl", "-f", "http://localhost:6333/readyz"] interval: 30s timeout: 10s retries: 5 # Web服务(旧版UI,新版已合并到api):仅用于兼容 web: image: difyai/dify-web:main ports: ["3000:3000"]

3.1 服务启动依赖链:为什么depends_on不能解决所有问题

depends_on只控制容器创建顺序,不保证服务内部已就绪。比如api服务依赖db,但PostgreSQL容器启动后,需要约5秒完成初始化(创建数据库、用户、表结构),此时api若立即连接,会报ConnectionRefusedError。Dify官方方案是:在apicommand中加入健康检查重试逻辑,但更稳妥的做法是用docker composeprofiles特性分阶段启动:

# 在docker-compose.yml顶部添加 profiles: - default - db-first # 修改db服务 db: profiles: ["db-first"] # ...其他配置不变 # 修改api服务,移除depends_on,改为条件启动 api: profiles: ["default"] # ...其他配置不变

然后分两步启动:

# 先启动数据库并等待就绪 docker compose --profile db-first up -d db # 等待PostgreSQL就绪(实测需12秒) sleep 12 # 再启动其余服务 docker compose up -d

踩坑实录:某次在阿里云ECS(2核4G)上部署,qdrant健康检查失败导致api无法启动。排查发现是qdrant容器内存不足——它默认尝试分配8GB内存,而宿主机只有4GB。解决方案不是加机器,而是修改qdrant服务配置:

qdrant: # ...其他配置 deploy: resources: limits: memory: 2G environment: - QDRANT__STORAGE__MMAP__ENABLED=false # 关闭内存映射,降低内存压力

3.2 网络模型:Docker内部DNS如何让服务互访像写localhost一样简单

Docker Compose自动创建一个名为<project_name>_default的自定义桥接网络,并为每个服务注册DNS记录。这意味着在api容器内,你可以直接用http://db:5432连接数据库,用redis://redis:6379连接缓存——Docker内置DNS会把dbredis解析为对应容器的IP地址(如172.20.0.3)。

但这里有个致命陷阱:如果你在宿主机上用curl http://localhost:8000测试API,返回404,不代表API没起来。因为localhost在宿主机上指向127.0.0.1,而API容器监听的是0.0.0.0:8000,但它的8000端口并未映射到宿主机(官方配置只映射了Nginx的80/443)。正确测试方式是:

# 进入api容器内部测试 docker compose exec api curl http://localhost:8000/health # 或者通过Nginx代理测试(这才是用户真实访问路径) curl http://localhost/health

这个设计体现了Dify的架构哲学:Nginx是唯一对外暴露的入口,所有流量经它路由到后端服务。这样既隐藏了内部服务端口,又便于统一处理HTTPS、CORS、静态资源缓存。

4. 从零部署实操:避开90%新手会踩的5个深坑

现在我们进入真正的部署环节。以下步骤基于Ubuntu 22.04 LTS(Linux 5.15内核),全程使用root用户操作。我会标注每个命令背后的原理,以及不这么做会引发什么问题。

4.1 环境初始化:磁盘、用户、防火墙的底层准备

很多教程跳过这步,直接git clone,结果部署到一半发现/var/lib/docker所在分区只剩2GB空间,Qdrant向量库构建直接失败。Dify知识库单文档切片后生成的向量数据,体积通常是原文的3-5倍。一个10MB的PDF,入库后可能占用30MB+磁盘。

# 检查根分区剩余空间(Dify最低要求20GB空闲) df -h / # 若空间不足,挂载新磁盘到/var/lib/docker(Docker数据根目录) # 创建新挂载点 sudo mkdir -p /mnt/docker-data # 格式化新磁盘(假设设备为/dev/sdb) sudo mkfs.ext4 /dev/sdb # 挂载 sudo mount /dev/sdb /mnt/docker-data # 永久挂载(写入/etc/fstab) echo '/dev/sdb /mnt/docker-data ext4 defaults 0 0' | sudo tee -a /etc/fstab # 配置Docker使用新路径 sudo mkdir -p /etc/docker echo '{"data-root": "/mnt/docker-data"}' | sudo tee /etc/docker/daemon.json # 重启Docker使配置生效 sudo systemctl restart docker

关键原理:Docker默认将镜像层、容器文件系统、卷数据全存在/var/lib/docker。Qdrant的向量索引是内存映射文件(mmap),需要大量连续磁盘空间。若根分区爆满,不仅Dify崩溃,整个系统可能因/var/log写满而无法登录。

接下来创建专用部署用户,绝不使用root直接运行Dify

# 创建dify用户,禁止shell登录(安全基线) sudo useradd -r -s /bin/false dify # 创建部署目录并授权 sudo mkdir -p /opt/dify sudo chown -R dify:dify /opt/dify # 切换用户(后续所有操作在此用户下进行) sudo -u dify -i

为什么不用root?Dify的api服务会加载用户上传的文件(如PDF、Word),若以root运行,恶意文件可能利用os.system()等函数执行宿主机命令。用受限用户运行,即使漏洞被利用,攻击面也大幅缩小。

4.2 获取并校验部署包:为什么git clone不是最佳选择

Dify官方发布页(https://github.com/langgenius/dify/releases)提供两种部署包:

  • source code:含全部源码,需自行构建镜像(适合二次开发);
  • docker-deploy:预编译的Docker Compose包(含docker-compose.ymlnginx.conf.env等)。

新手应选后者,但要注意:直接wget下载的zip包可能被中间劫持。正确做法是验证GPG签名:

# 下载发布页的SIGNATURE文件和公钥 wget https://github.com/langgenius/dify/releases/download/v0.6.3/docker-deploy-v0.6.3.zip wget https://github.com/langgenius/dify/releases/download/v0.6.3/docker-deploy-v0.6.3.zip.SIGNATURE # 导入Dify官方GPG公钥(从官网获取,非第三方) gpg --import dify-official-public-key.asc # 验证签名 gpg --verify docker-deploy-v0.6.3.zip.SIGNATURE docker-deploy-v0.6.3.zip # 解压到部署目录 unzip docker-deploy-v0.6.3.zip -d /opt/dify/

4.3 配置文件定制化:.env文件里的12个关键参数详解

Dify的.env文件是部署成败的核心。官方模板里有30+参数,但90%的失败源于以下12个未正确设置:

参数名默认值必须修改?说明实例值
COMPOSE_PROJECT_NAMEdifyDocker项目名,影响网络和卷前缀mydify
API_PORT8000API服务监听端口(内部用,不映射)8000
NGINX_PORT80Nginx对外HTTP端口8080(若80被占用)
NGINX_SSL_PORT443HTTPS端口(需证书)8443
DATABASE_HOSTdb数据库服务名(Docker DNS名)db
DATABASE_PORT5432PostgreSQL端口5432
REDIS_HOSTredisRedis服务名redis
REDIS_PORT6379Redis端口6379
QDRANT_HOSTqdrantQdrant服务名qdrant
QDRANT_PORT6333Qdrant端口6333
SECRET_KEYchangethissecretkey必须Django密钥,影响会话加密$(openssl rand -base64 32)
SSL_CERT_PATH./ssl/fullchain.pemHTTPS证书路径(启用SSL时必填)/opt/dify/ssl/fullchain.pem

生成强密钥的命令(在.env中直接写):

# 在终端执行,结果粘贴到SECRET_KEY= openssl rand -base64 32 | tr -d '\n'; echo

重要提醒:SECRET_KEY一旦设定,就不能再改!否则所有用户会话失效,需强制登出。我曾因测试环境密钥泄露,紧急轮换后收到23个客户投诉“账号登不上”,根源就是这个值。

4.4 启动与验证:用5条命令确认系统健康度

执行启动命令前,确保当前目录是/opt/dify

cd /opt/dify # 启动所有服务(后台运行) docker compose up -d # 查看服务状态(重点关注STATUS列) docker compose ps # 检查各服务日志(过滤ERROR) docker compose logs api \| grep -i "error\|exception\|failed" # 测试API健康端点(通过Nginx代理) curl -I http://localhost/health # 测试Qdrant向量库(直接访问容器端口) curl http://localhost:6333/readyz

docker compose ps输出应类似:

NAME COMMAND SERVICE STATUS PORTS dify-api-1 "uvicorn app.main:..." api running (healthy) 8000/tcp dify-db-1 "docker-entrypoint.s…" db running (healthy) 5432/tcp dify-nginx-1 "/docker-entrypoint.…" nginx running (healthy) 0.0.0.0:80->80/tcp, :::80->80/tcp dify-qdrant-1 "/qdrant-docker-ent…" qdrant running (healthy) 6333/tcp dify-redis-1 "docker-entrypoint.s…" redis running (healthy) 6379/tcp dify-worker-1 "celery -A app.work…" worker running (healthy) 8000/tcp

注意:STATUS列显示running (healthy)才是真健康。若显示running (health: starting),说明健康检查未通过,需等30秒再查;若一直卡在此状态,用docker compose logs qdrant看具体错误。

4.5 故障排查黄金路径:当docker compose up卡住时,按此顺序检查

部署中最常见的现象是:docker compose up -d执行后,终端卡住不动,或docker compose ps显示多个服务Restarting。这不是网络慢,而是服务间依赖未满足。按此顺序排查:

第一步:检查Docker守护进程状态

sudo systemctl status docker # 若显示inactive,启动它 sudo systemctl start docker

第二步:检查Docker Compose是否识别配置文件

# 确认当前目录有docker-compose.yml ls -l docker-compose.yml # 检查文件格式(YAML对缩进极其敏感) yamllint docker-compose.yml # 若未安装,pip3 install yamllint

第三步:检查镜像拉取是否被阻塞

# 查看Docker守护进程日志 sudo journalctl -u docker --since "1 hour ago" | grep -i "pull\|error" # 若卡在`pulling from docker.io`,配置阿里云镜像加速器 sudo mkdir -p /etc/docker echo '{"registry-mirrors": ["https://<your-code>.mirror.aliyuncs.com"]}' | sudo tee /etc/docker/daemon.json sudo systemctl restart docker

第四步:检查端口冲突

# Dify默认占80/443/6333/5432/6379,检查是否被占用 sudo ss -tuln \| grep -E ':(80|443|6333|5432|6379)' # 若被占用,修改`.env`中对应端口,或杀掉占用进程 sudo kill -9 <PID>

第五步:检查磁盘inode是否耗尽(易忽略!)

# Qdrant和PostgreSQL产生海量小文件,可能inode用尽 df -i / # 若Use%接近100%,清理无用文件或扩容

实战案例:某次在腾讯云CVM上部署,qdrant一直Restartingdocker compose logs qdrant显示Permission denied: '/qdrant/storage'。排查发现是/mnt/docker-data挂载时未加exec选项(默认noexec),导致Qdrant无法执行二进制。解决方案:

# 重新挂载,添加exec权限 sudo mount -o remount,exec /mnt/docker-data # 写入fstab永久生效 sudo sed -i 's/defaults/defaults,exec/' /etc/fstab

5. 生产就绪加固:让Dify在Linux上真正“稳如磐石”

部署成功只是开始。Dify作为企业级AI应用平台,必须满足7×24小时可用、数据持久化、故障自愈、安全审计等生产要求。以下是我为客户落地的5项加固措施,每项都经过线上环境验证。

5.1 自动重启策略:restart: always的双刃剑效应

Docker Compose的restart: always策略能让容器崩溃后自动拉起,但若服务因内存溢出(OOM)被内核杀死,盲目重启只会加剧问题。Dify的api服务在处理大文件知识库时,内存峰值可达1.5GB,而qdrant在构建索引时可能冲到3GB。

正确做法是:为每个服务设置内存限制 + OOM优先级调整。修改docker-compose.ymlapiqdrant服务:

api: # ...其他配置 deploy: resources: limits: memory: 2G cpus: '1.0' reservations: memory: 1G # 设置OOM Score Adj,降低被kill概率(数值越小越不易被kill) mem_reservation: 1G mem_limit: 2G qdrant: # ...其他配置 deploy: resources: limits: memory: 3G cpus: '2.0' # Qdrant对内存敏感,需更高OOM优先级 mem_reservation: 2G mem_limit: 3G

原理:Linux内核OOM Killer根据/proc/<pid>/oom_score_adj值决定杀哪个进程,范围-1000(永不杀)到1000(优先杀)。Docker默认设为0,我们通过mem_reservation间接降低该值。实测后,qdrant在内存紧张时不再被随机kill,而是优雅降级(返回503)。

5.2 日志集中管理:用docker compose logs无法替代ELK

Docker默认日志驱动是json-file,日志存在/var/lib/docker/containers/<id>/<id>-json.log,单个文件超100MB后滚动,但不自动清理。Dify的worker服务每处理一个知识库切片就写10+行日志,一个月下来日志占满20GB磁盘。

生产环境必须配置日志轮转:

# 在docker-compose.yml顶层添加 logging: driver: "json-file" options: max-size: "10m" max-file: "3" # 为每个服务显式指定(避免继承全局配置) api: logging: driver: "json-file" options: max-size: "20m" max-file: "5"

但更进一步,应接入ELK(Elasticsearch+Logstash+Kibana)或Loki。我为客户部署的轻量方案是:用fluentd收集日志并推送到腾讯云CLS(日志服务):

# 创建fluentd配置 cat > /opt/dify/fluentd.conf << 'EOF' <source> @type tail path /var/lib/docker/containers/*/*-json.log pos_file /var/log/fluentd-docker.pos tag docker.* format json time_key time time_format %Y-%m-%dT%H:%M:%S.%NZ </source> <filter docker.var.lib.docker.containers.*.*-json.log> @type record_transformer <record> container_id ${tag_parts[3]} </record> </filter> <match docker.**> @type forward <server> host cls.tencentcs.com port 10000 </server> </match> EOF # 启动fluentd容器 docker run -d \ --name fluentd \ -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ -v /var/log:/var/log \ -v /opt/dify/fluentd.conf:/fluentd/etc/fluent.conf:ro \ -p 24224:24224 \ -p 24224:24224/udp \ fluent/fluentd

5.3 备份与恢复:Dify数据的3个层次及对应策略

Dify的数据分为三层,备份策略完全不同:

数据层存储位置变更频率备份策略恢复时间目标(RTO)
结构化数据(用户、应用、会话)PostgreSQL数据库中(每小时数次)pg_dump每日全备 + WAL归档<5分钟
向量数据(知识库嵌入)Qdrant存储目录低(知识库更新才变)rsync增量同步到NAS<30分钟
静态资源(上传的原始文件)./volumes/storage低(用户上传才变)rclone同步到对象存储<10分钟

PostgreSQL备份脚本(/opt/dify/backup-db.sh):

#!/bin/bash DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/opt/dify/backups/db" mkdir -p $BACKUP_DIR # 使用pg_dump导出(需在db容器内执行) docker compose exec -T db pg_dump -U postgres -d dify > $BACKUP_DIR/dify_db_$DATE.sql # 压缩并删除7天前备份 gzip $BACKUP_DIR/dify_db_$DATE.sql find $BACKUP_DIR -name "dify_db_*.sql.gz" -mtime +7 -delete

Qdrant备份(/opt/dify/backup-qdrant.sh):

#!/bin/bash # Qdrant数据在./volumes/qdrant,直接rsync rsync -avz --delete /opt/dify/volumes/qdrant/ /backup/qdrant/

关键经验:绝不要用docker volume backup命令!Dify的qdrant卷是直接挂载的宿主机目录,docker volume命令对其无效。必须操作宿主机路径。

5.4 安全加固:关闭Dify的危险功能开关

Dify默认开启一些便利但高危的功能,生产环境必须禁用:

  1. 关闭调试模式.envDEBUG=false(默认false,但需确认);
  2. 禁用Jinja2模板注入:在api服务的environment中添加:
    environment: - TEMPLATING_ENABLED=false # 禁用用户自定义模板
  3. 限制文件上传类型:修改nginx.conf,在location /files/块中添加:
    location /files/ { # ...原有配置 if ($request_filename ~* \.(php|pl|py|jsp|sh|cgi|exe|bat)$) { return 403; } }
  4. 启用HTTPS强制跳转:在nginx.conf的HTTP server块中添加:
    return 301 https://$host$request_uri;

5.5 在线升级:Dify版本迭代时的无缝切换方案

Dify更新频繁(平均每月2次大版本),但docker compose pull && up -d会导致服务中断。我的升级方案是蓝绿部署

  1. 下载新版本部署包到/opt/dify-v0.6.4
  2. 修改新目录下的.env