Tropic Host

Hướng Dẫn Cài Đặt Dify AI Trên VPS KVM Bằng Docker: Xây Dựng RAG và AI Agent Riêng

50 phút đọc
Tropic

Tóm tắt nhanh: Để triển khai cụm microservices Dify AI (API, Celery worker, PostgreSQL pgvector, Redis) đạt chuẩn production trên KVM VPS, hệ thống yêu cầu cấu hình tối thiểu 4 vCPU, 8 GB RAM (cấu hình thêm 4 GB swap dự phòng OOM khi chunking dữ liệu RAG), 60 GB NVMe PCIe 4.0 và đường truyền 1 Gbps kích hoạt thuật toán TCP BBR. Toàn bộ stack Docker Compose bắt buộc định tuyến qua Nginx reverse proxy hỗ trợ TLS 1.3, HTTP/2 và tắt hoàn toàn bộ đệm (proxy_buffering off) nhằm duy trì ổn định luồng Server-Sent Events (SSE) cho token streaming thời gian thực. Việc nạp tham số kernel vm.max_map_count=262144 cùng giới hạn cgroups v2 chặt chẽ là điều kiện tiên quyết để triệt tiêu hiện tượng CPU Steal Time (%st), ngăn chặn kernel OOM Killer và giữ p99 query latency của vector database dưới mức 50ms.


Mục lục

  1. Yêu Cầu Phần Cứng và Phân Bổ Tài Nguyên VPS Cho Dify AI
  2. Chuẩn Bị Hệ Điều Hành Linux và Tinh Chỉnh Tham Số Bộ Nhớ Ảo
  3. Triển Khai Dify AI Bằng Docker Compose Với Bộ Nhớ Lưu Trữ Bền Vững
  4. Cấu Hình Nginx Reverse Proxy Với Chứng Chỉ SSL Let's Encrypt
  5. Kết Nối Mô Hình Ngôn Ngữ Lớn và Tích Hợp Cơ Sở Dữ Liệu Vector RAG
  6. Quy Trình Sao Lưu Tự Động và Khôi Phục Thảm Họa (Disaster Recovery)
  7. Câu hỏi thường gặp (FAQ)

Yêu Cầu Phần Cứng và Phân Bổ Tài Nguyên VPS Cho Dify AI

Kiến trúc của Dify không vận hành dưới dạng một monolithic container duy nhất, mà là một cụm phân tán gồm tối thiểu 9 microservices độc lập: Nginx reverse proxy, Dify API (Flask/Gunicorn), Dify Celery Worker (xử lý tác vụ nền), Dify Web (Next.js frontend), PostgreSQL 15+ (lưu trữ metadata, logs, cấu hình DSL), Redis 7 (task broker & cache), Vector Database (mặc định Milvus Standalone đi kèm etcd và MinIO, hoặc Qdrant/Weaviate), Dify Sandbox (thực thi Python code an toàn) và SSRF Proxy.

Khi thực hiện cài dify ai trên vps docker, việc tính toán sai lệch dung lượng RAM hoặc thiếu hụt IOPS lưu trữ sẽ lập tức kích hoạt cơ chế Linux OOM (Out-Of-Memory) Killer, khiến tiến trình Celery worker bị giải phóng đột ngột hoặc làm hỏng chỉ mục vector HNSW ngay trong giai đoạn embedding tập dữ liệu lớn.

+-----------------------------------------------------------------------------------+
|                                 Nginx Reverse Proxy                               |
+--------+--------------------------+-----------------------+-----------------------+
         |                          |                       |
         v                          v                       v
+------------------+      +------------------+    +--------------------+
|  Dify Web UI     |      |   Dify API       |    | Dify Celery Worker |
|  (Next.js Node)  |      |   (Gunicorn/Py)  |    | (Chunking/RAG)     |
+------------------+      +--------+---------+    +---------+----------+
                                   |                        |
         +-------------------------+------------------------+
         |                         |                        |
         v                         v                        v
+------------------+      +------------------+    +--------------------+
|  PostgreSQL 15   |      |  Redis 7 Cache   |    | Milvus Standalone  |
|  (Metadata/Logs) |      |  (Broker/Queue)  |    | (etcd + MinIO)     |
+------------------+      +------------------+    +--------------------+

Phân Tích Footprint Tài Nguyên Từng Microservice

Để ngăn chặn tình trạng thắt cổ chai tài nguyên, mọi thành phần trong stack Docker Compose cần được cấp phát định mức vCPU, RAM và IOPS cụ thể:

  1. Dify API & Celery Worker (api / worker):
  2. RAM: Tối thiểu 1.5 GB cho API và 2.0 GB – 4.0 GB cho Celery Worker. Celery Worker là thành phần ngốn tài nguyên nhất khi phân tích cú pháp tài liệu (PDF OCR, docx, trích xuất bảng biểu) và gọi API embedding. Nếu ingest đồng thời nhiều file nặng, worker sẽ chiếm dụng 100% core xử lý.
  3. vCPU: Cần tối thiểu 2 vCPU vật lý hoặc vCPU dedicated để tránh rớt WebSocket/SSE stream phản hồi token về client.
  4. PostgreSQL (db):
  5. RAM: Tụ điểm dữ liệu quan hệ của hệ thống. Yêu cầu tối thiểu 1.0 GB RAM cho instance nhỏ và 2.0 – 4.0 GB cho môi trường production.
  6. Lưu trữ: Phụ thuộc vào retention policy của chat logs và execution traces. Cần cấu hình shared_buffers = 512MB và work_mem = 16MB để xử lý các câu truy vấn phân tích lịch sử hội thoại phức tạp.
  7. Redis (redis):
  8. RAM: 512 MB – 1.5 GB. Đóng vai trò message broker cho Celery và bộ nhớ đệm cache session. Bắt buộc cấu hình chính sách giải phóng bộ nhớ maxmemory-policy volatile-lru để tránh tràn RAM khi hàng đợi tác vụ nền tăng đột biến.
  9. Vector Database — Milvus Standalone (milvus-standalone, etcd, minio):
  10. RAM: Đây là thành phần tiêu tốn RAM nghiêm ngặt nhất. Milvus tải toàn bộ chỉ mục vector (HNSW graph) vào RAM để đạt độ trễ tìm kiếm tương đồng (similarity search) dưới 15ms. Cụm ba container (Milvus core, etcd metadata, MinIO object storage) tiêu tốn tối thiểu 3.0 GB RAM ở trạng thái rảnh và dễ dàng chạm ngưỡng 6.0 – 8.0 GB khi chỉ mục vượt quá 500.000 vectors (kích thước 1536 chiều của OpenAI text-embedding-3-small hoặc 1024 chiều của BAAI/bge-large).
  11. IOPS: MinIO yêu cầu tốc độ đọc/ghi ngẫu nhiên cao để lưu trữ các segment dữ liệu raw vector.
  12. Dify Web UI & Sandbox (web, sandbox, ssrf_proxy):
  13. RAM: Tổng cộng khoảng 500 MB – 1.0 GB. Node.js runtime của frontend tiêu tốn khoảng 300 MB, trong khi Sandbox sử dụng cgroups nội bộ để cô lập mã nguồn Python với giới hạn trần mặc định 256 MB.

Ma Trận Định Cỡ (Sizing Matrix) Hạ Tầng Cho Dify AI

Bảng thông số kỹ thuật chuẩn hóa dựa trên tải thực tế và kích thước kho dữ liệu Knowledge Base:

Quy Mô Triển Khai Người Dùng Đồng Thời Số Lượng Vector Tri Thức Cấu Hình vCPU & Kiến Trúc RAM Tối Thiểu / Đề Xuất Yêu Cầu NVMe Storage & IOPS Vector DB Tối Ưu Cấu Hình Tham Chiếu KVM tropic.host
PoC / Thử Nghiệm 1 – 3 < 50.000 vectors 2 vCPU (AMD/Intel) 4 GB / 6 GB 40 GB NVMe (IOPS > 10.000) Pgvector hoặc Qdrant TR-KVM-Starter: 2 vCPU, 4 GB RAM, 50 GB NVMe
Production Nhóm Nhỏ 5 – 25 50.000 – 500.000 4 vCPU (High-Freq ≥ 3.0 GHz) 8 GB / 12 GB 80 GB NVMe (IOPS > 30.000) Milvus Standalone / Qdrant TR-KVM-Pro: 4 vCPU, 8 GB RAM, 100 GB NVMe PCIe 4.0
Enterprise / Multi-Agent 50 – 200+ > 1.000.000 vectors 8 – 16 vCPU (AMD EPYC / Ryzen 9) 16 GB / 32 GB 200+ GB NVMe (IOPS > 50.000) Milvus Standalone Cluster TR-KVM-Enterprise: 8 vCPU, 16–32 GB RAM, 250 GB NVMe
Cảnh báo phần cứng: Nền tảng KVM ảo hóa tại tropic.host đảm bảo tỷ lệ tranh chấp tài nguyên bằng không với thông số CPU Steal Time đo lường thực tế %st = 0.0%. Trên các hạ tầng ảo hóa giá rẻ dùng OpenVZ hoặc KVM bị overcommit CPU nặng, khi Celery worker tính toán chunking hoặc Milvus quét đồ thị HNSW, %st có thể vọt lên > 15%, gây nghẽn toàn bộ luồng xử lý và đẩy độ trễ p99 của API lên hàng chục giây.

Tối Ưu Kernel Phục Vụ Vector Search Và Redis

Trước khi khởi chạy stack container, nhân Linux trên máy chủ host cần được áp dụng các tham số sysctl chuẩn cho database và container density:

# Tạo file cấu hình sysctl chuyên dụng cho cụm Dify
cat << 'EOF' | sudo tee /etc/sysctl.d/99-dify-performance.conf
# Cho phép Milvus và Weaviate tạo đủ vùng ánh xạ bộ nhớ (memory-mapped areas)
vm.max_map_count=262144

# Ngăn chặn lỗi Redis BGSAVE khi thiếu bộ nhớ cấp phát ảo
vm.overcommit_memory=1

# Giảm xu hướng tráo đổi bộ nhớ sang swap để bảo vệ độ trễ IOPS
vm.swappiness=10

# Nâng cao giới hạn file descriptors cho Nginx và PostgreSQL
fs.file-max=2097152

# Tối ưu hàng đợi socket cho lưu lượng truy cập API cao
net.core.somaxconn=4096
net.ipv4.tcp_max_syn_backlog=4096

# Kích hoạt thuật toán điều khiển tắc nghẽn BBR
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF

# Nạp cấu hình sysctl vào runtime
sudo sysctl --system

Tham số vm.max_map_count=262144 là điều kiện tiên quyết bắt buộc. Nếu giữ nguyên giá trị mặc định của Ubuntu (65530), container milvus-standalone sẽ crash liên tục với mã lỗi SIGSEGV ngay khi khởi tạo tập chỉ mục IVF_FLAT hoặc HNSW.


Giới Hạn Tài Nguyên Cgroups Trong docker-compose.override.yml

Để bảo vệ máy chủ khỏi nguy cơ cạn kiệt tài nguyên diện rộng dẫn đến mất kết nối SSH, tạo file docker-compose.override.yml nhằm thiết lập ngưỡng trần (limits) và cam kết tài nguyên tối thiểu (reservations) cho từng dịch vụ cốt lõi:

version: '3.8'

services:
  api:
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2048M
        reservations:
          cpus: '0.5'
          memory: 512M

  worker:
    deploy:
      resources:
        limits:
          cpus: '4.0'
          memory: 4096M
        reservations:
          cpus: '1.0'
          memory: 1024M

  db:
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2048M
        reservations:
          cpus: '0.5'
          memory: 512M
    shm_size: '512mb'

  redis:
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1024M
        reservations:
          cpus: '0.2'
          memory: 256M

  milvus-standalone:
    deploy:
      resources:
        limits:
          cpus: '4.0'
          memory: 6144M
        reservations:
          cpus: '1.0'
          memory: 2048M

Ghi chú kỹ thuật: Khai báo shm_size: '512mb' trên PostgreSQL là bắt buộc. Giá trị mặc định 64mb của Docker daemon sẽ khiến PostgreSQL văng lỗi could not resize shared memory segment khi thực thi các câu lệnh sort và join lớn trong các bảng messages và conversations.


Kiểm Tra Mức Độ Sử Dụng Phần Cứng Trên VPS

Sau khi hoàn tất quá trình khởi tạo container, kỹ sư vận hành kiểm tra tình trạng tải thực tế qua terminal bằng bộ công cụ chuẩn:

# Kiểm tra CPU Steal Time (đảm bảo %st bằng 0.0)
vmstat 1 5

# Giám sát dung lượng RAM tiêu thụ tức thời của từng container
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.BlockIO}}"

# Đo đạc hiệu năng I/O ngẫu nhiên 4K QD1 trên ổ cứng NVMe
fio --name=random-read-write --ioengine=posixaio --rw=randrw --bs=4k --size=1G --numjobs=1 --iodepth=1 --runtime=30 --time_based --rwmixread=75 --group_reporting

Tại hạ tầng KVM của tropic.host, việc trang bị ổ cứng NVMe PCIe 4.0 doanh nghiệp cho phép throughput đạt chuẩn trên 50.000 IOPS cho các truy vấn 4K ngẫu nhiên, loại bỏ hoàn toàn hiện tượng nghẽn I/O wait (%iowait > 5%) thường gặp khi ghi đồng thời dữ liệu vector vào MinIO và dữ liệu quan hệ vào PostgreSQL.

Chuẩn Bị Hệ Điều Hành Linux và Tinh Chỉnh Tham Số Bộ Nhớ Ảo

Trước khi kéo các Docker image và khởi chạy cụm container, bước tối quan trọng khi cài Dify AI trên VPS Docker là tái cấu hình nhân Linux (kernel tuning) và bộ nhớ ảo (Virtual Memory). Dify là một hệ thống microservices phức hợp bao gồm Web frontend, API backend (Python/Celery), Redis cache, cơ sở dữ liệu quan hệ PostgreSQL và hệ thống lưu trữ vector (Vector Database) như Weaviate, Qdrant hoặc Milvus. Cấu hình mặc định của các bản phân phối Ubuntu Server hoặc Debian thường được tối ưu hóa cho các tác vụ tổng quát nhẹ, không đáp ứng được đặc thù cấp phát bộ nhớ của các cơ sở dữ liệu vector và hàng đợi message bất đồng bộ dưới tải cao.

Khác với môi trường ảo hóa dạng vùng chứa (LXC/OpenVZ) vốn bị khóa quyền can thiệp vào /proc/sys/ từ máy chủ mẹ, nền tảng KVM độc lập tại tropic.host trao toàn quyền kiểm soát nhân Linux (guest kernel). Điều này cho phép áp dụng trực tiếp các chỉ thị sysctl và ulimits cấp hệ thống mà không gặp rào cản bảo mật từ hypervisor.


1. Tinh Chỉnh vm.max_map_count Cho Vector Database

Trọng tâm của tính năng RAG (Retrieval-Augmented Generation) trong Dify nằm ở cơ sở dữ liệu vector. Các engine vector (đặc biệt là Weaviate hoặc Elasticsearch/Milvus nếu mở rộng) sử dụng cơ chế ánh xạ bộ nhớ trực tiếp từ tệp tin (mmap - Memory-Mapped Files) nhằm tải các cấu trúc đồ thị vector HNSW (Hierarchical Navigable Small World) từ ổ cứng NVMe vào không gian địa chỉ ảo của tiến trình.

Giá trị mặc định của tham số vm.max_map_count trên Linux chỉ dừng lại ở mức 65530. Con số này đại diện cho số lượng tối đa các vùng bộ nhớ ảo (Virtual Memory Areas - VMA) mà một tiến trình có thể sở hữu. Khi Dify thực hiện chunking tài liệu, nhúng embedding vector 1536 chiều (từ mô hình OpenAI text-embedding-3-small) hoặc 3072 chiều và lập chỉ mục cho hàng chục nghìn đoạn văn bản, số lượng phân đoạn bộ nhớ VMA ánh xạ qua mmap sẽ nhanh chóng chạm ngưỡng 65.530.

Hậu quả khi không thay đổi tham số: * Container vector database bị dừng khẩn cấp bởi tín hiệu SIGKILL hoặc báo lỗi nghiêm trọng trong log: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]. * Lỗi OutOfMemoryError: Map failed xuất hiện, khiến toàn bộ tiến trình truy vấn tri thức trong Dify rơi vào trạng thái timeout liên tục (HTTP 504 Gateway Timeout).

Đối với môi trường production xử lý tài liệu lớn, tham số này phải được nâng lên tối thiểu 262144, hoặc lý tưởng là 1048576 nếu VPS sở hữu từ 16GB RAM trở lên:

# Kiểm tra giá trị hiện thời
sysctl vm.max_map_count

# Thiết lập tức thời trong runtime
sudo sysctl -w vm.max_map_count=262144

2. Tối Ưu Quản Lý Phân Trang Bộ Nhớ (Swappiness và Overcommit)

Hệ thống worker của Dify dựa trên Celery và Redis để xử lý hàng loạt tác vụ nền: trích xuất văn bản từ PDF/DOCX, gọi API ngoài qua mô hình ngôn ngữ lớn (LLM), và tính toán vector. Hai tham số kernel quản lý hành vi phân bổ RAM bắt buộc phải được chuẩn hóa:

vm.overcommit_memory = 1

Redis yêu cầu cấp phát bộ nhớ ảo vượt mức (memory overcommit) để thực thi cơ chế sao lưu nền BGSAVE và tối ưu tệp nhật ký BGREWRITEAOF. Khi Redis tạo tiến trình con thông qua lời gọi hệ thống fork(), nhân Linux mặc định (vm.overcommit_memory = 0) sẽ ước tính xem hệ thống có đủ lượng RAM trống để sao chép toàn bộ trang bộ nhớ của Redis hay không. Nếu dung lượng RAM khả dụng thấp hơn dung lượng Redis đang chiếm giữ, fork() sẽ thất bại ngay lập tức, sinh ra lỗi Can't save in background: fork: Cannot allocate memory.

Gán vm.overcommit_memory = 1 chỉ thị cho kernel luôn chấp thuận các yêu cầu cấp phát bộ nhớ ảo, dựa trên cơ chế Copy-on-Write (CoW). Điều này đảm bảo tiến trình nền của Redis không bị chặn đứng khi RAM vật lý đạt ngưỡng cao.

vm.swappiness = 10

Mặc định trên Ubuntu, vm.swappiness được đặt ở mức 60. Khi thông số này quá cao, kernel sẽ chủ động đẩy các trang bộ nhớ ẩn của tiến trình API Dify và Gunicorn ra phân vùng swap trên đĩa dù RAM thực vẫn còn trống. Việc hoán đổi trang (paging) này tạo ra hiện tượng trễ tức thời (latency spike) lên tới hàng trăm mili-giây cho mỗi truy vấn API. Đặt giá trị về 10 buộc kernel ưu tiên giữ toàn bộ tiến trình ứng dụng trong RAM, chỉ kích hoạt swap khi hệ thống chịu áp lực bộ nhớ cực hạn.

vm.dirty_ratio = 15 và vm.dirty_background_ratio = 5

Trong quá trình upload tệp tài liệu dung lượng lớn vào MinIO hoặc xử lý tệp nhị phân, các trang nhớ bẩn (dirty pages) liên tục được ghi vào bộ đệm của kernel. Việc hạ thấp dirty_background_ratio xuống 5% và dirty_ratio xuống 15% kích hoạt các luồng ghi ngầm pdflush/flush của kernel xả dữ liệu xuống đĩa sớm hơn và đều đặn hơn. Kết hợp với băng thông ghi ngẫu nhiên trên 50.000 IOPS của ổ cứng NVMe PCIe 4.0 tại tropic.host, cơ chế này ngăn chặn hiện tượng đóng băng I/O (I/O freeze) toàn bộ hệ thống khi MinIO và PostgreSQL cùng ghi đồng thời lượng lớn dữ liệu.


3. Tinh Chỉnh Ngăn Xếp Mạng (TCP Network Stack) Cho Luồng Dữ Liệu SSE

Dify liên tục duy trì các kết nối HTTP tồn tại lâu (Long-lived HTTP connections) để hỗ trợ tính năng truyền phát phản hồi thời gian thực qua Server-Sent Events (SSE). Hàng trăm người dùng gửi câu hỏi cùng lúc đồng nghĩa với hàng trăm kết nối streaming mở đồng thời tới Nginx và cụm Dify API.

Để tránh hiện tượng tràn hàng đợi SYN và từ chối kết nối (connection reset by peer), ngăn xếp TCP cần được mở rộng:

  • net.core.somaxconn = 65535: Mở rộng hàng đợi lắng nghe kết nối socket tối đa cho Nginx và Docker bridge, vượt qua mức mặc định hạn chế là 128 hoặc 4096.
  • net.ipv4.tcp_max_syn_backlog = 65535: Dung lượng bộ đệm lưu trữ các yêu cầu bắt tay TCP 3 bước (SYN packets) chưa hoàn tất.
  • net.ipv4.ip_local_port_range = 1024 65535: Mở rộng dải cổng cục bộ tạm thời (ephemeral ports) để các container microservices trong mạng nội bộ Docker có thể mở socket giao tiếp liên tục mà không bị cạn kiệt port.
  • net.ipv4.tcp_tw_reuse = 1: Tái sử dụng các socket đang ở trạng thái TIME_WAIT cho các kết nối mới khi an toàn về mặt giao thức TCP.
  • net.core.default_qdisc = fq và net.ipv4.tcp_congestion_control = bbr: Kích hoạt thuật toán điều khiển tắc nghẽn BBR của Google thay cho CUBIC truyền thống. Hạ tầng mạng tại tropic.host hỗ trợ sẵn BBR trên các cổng uplink 1–10 Gbps, giúp duy trì throughput tối đa và giảm thiểu tình trạng rớt gói tin (packet loss) khi Dify streaming token từ các API server đặt tại Frankfurt, Amsterdam hay Singapore.

4. Triển Khai Cấu Hình Sysctl Tự Động

Tạo một tệp cấu hình tập trung để lưu trữ toàn bộ các tham số đã phân tích, đảm bảo các thiết lập này tự động có hiệu lực sau mỗi lần khởi động lại máy chủ:

sudo tee /etc/sysctl.d/99-dify-production.conf << 'EOF'
# Tối ưu hóa bộ nhớ ảo cho Vector Database và Redis
vm.max_map_count = 262144
vm.overcommit_memory = 1
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5

# Tối ưu hóa ngăn xếp mạng cho kết nối đồng thời và SSE
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 10000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65535

# Tối ưu hóa bộ đệm socket đọc/ghi (Buffer sizing)
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# Kích hoạt thuật toán tắc nghẽn TCP BBR
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# Quản lý giới hạn file hệ thống
fs.file-max = 2097152
EOF

# Nạp toàn bộ cấu hình mới vào kernel
sudo sysctl --system

Kiểm tra xác thực việc áp dụng BBR và max_map_count:

sysctl net.ipv4.tcp_congestion_control
# Kết quả mong đợi: net.ipv4.tcp_congestion_control = bbr

sysctl vm.max_map_count
# Kết quả mong đợi: vm.max_map_count = 262144

5. Nâng Hạn Mức Tệp Tin (File Descriptors) Cấp Hệ Thống và Docker Daemon

Trong Linux, mọi socket mạng, tệp log, kết nối cơ sở dữ liệu và đường ống dữ liệu (pipe) giữa các container đều được quản lý dưới dạng tệp tin (File Descriptor - FD).

Mức giới hạn mềm mặc định cho một phiên người dùng thường là 1024. Trong kịch bản triển khai Dify AI, một worker Celery xử lý song song 8 tác vụ nhúng, đồng thời mở kết nối đến MinIO, Redis, PostgreSQL và gọi các web API ra ngoài sẽ nhanh chóng làm cạn kiệt giới hạn 1024, gây ra lỗi hệ thống nghiêm trọng: IOError: [Errno 24] Too many open files.

Cần mở rộng giới hạn này tại hai tầng: phiên PAM hệ thống và trực tiếp trong Docker Engine.

Cấu hình /etc/security/limits.conf

Chỉnh sửa giới hạn tài nguyên hệ thống cho tất cả người dùng và tiến trình chạy ngầm:

sudo tee /etc/security/limits.d/99-dify.conf << 'EOF'
* soft nofile 65536
* hard nofile 1048576
* soft nproc 65536
* hard nproc 65536
root soft nofile 65536
root hard nofile 1048576
EOF

Cấu hình Docker Daemon (daemon.json)

Ngay cả khi hệ điều hành máy chủ đã nâng mức nofile, các container Docker được khởi tạo vẫn có thể bị áp mức ulimit cô lập từ Docker daemon. Cần can thiệp vào /etc/docker/daemon.json để truyền thẳng các hạn mức cấp cao vào mọi container con:

sudo tee /etc/docker/daemon.json << 'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "3"
  },
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 1048576,
      "Soft": 65536
    },
    "nproc": {
      "Name": "nproc",
      "Hard": 65536,
      "Soft": 65536
    }
  }
}
EOF

# Tải lại cấu hình và khởi động lại Docker service
sudo systemctl daemon-reload
sudo systemctl restart docker

Xác minh giới hạn thực thi ngay bên trong môi trường Docker bằng một container kiểm tra tạm thời:

docker run --rm alpine sh -c "ulimit -n"

Đầu ra trả về giá trị 65536 chứng minh mọi container trong cụm Dify triển khai sau đó sẽ có đủ không gian tài nguyên để duy trì tải kết nối lớn, loại bỏ hoàn toàn các lỗi nghẽn socket và treo hàng đợi trước khi bước vào giai đoạn cấu hình tệp docker-compose.yaml.

Triển Khai Dify AI Bằng Docker Compose Với Bộ Nhớ Lưu Trữ Bền Vững

Khi thực hiện quy trình cài dify ai trên vps docker trong môi trường production, kiến trúc lưu trữ dữ liệu bền vững (persistent storage) và quản trị biến môi trường là yếu tố sống còn. Dify không phải là một container đơn lẻ mà là một cụm microservices đồng bộ: Web frontend, API backend, Celery asynchronous workers, Celery beat scheduler, PostgreSQL (kèm extension pgvector), Redis cache, Sandbox môi trường thực thi mã nguồn an toàn, và Nginx gateway.

Việc để dữ liệu ghi trên phân vùng tạm thời (ephemeral storage) của container sẽ dẫn đến thảm họa mất trắng cơ sở dữ liệu vector, lịch sử hội thoại và cấu hình LLM ngay khi container được recreate hoặc cập nhật image mới.

Chuẩn bị cấu trúc thư mục và phân quyền phân vùng dữ liệu

Cần tải mã nguồn triển khai chính thức của Dify trực tiếp từ kho lưu trữ GitHub và cố định cấu trúc thư mục làm việc tiêu chuẩn tại /opt/dify:

# Tạo thư mục gốc và clone repository chính thức của Dify
sudo mkdir -p /opt/dify
sudo git clone --depth 1 https://github.com/langgenius/dify.git /opt/dify

# Di chuyển trực tiếp vào thư mục chứa cấu hình Docker
cd /opt/dify/docker

Mặc định, Dify sử dụng các Docker Named Volumes hoặc bind mounts cục bộ để lưu trữ trạng thái. Trong môi trường production tải cao trên nền tảng KVM VPS của tropic.host (https://tropic.host), việc gắn kết trực tiếp các thư mục bind mount lên ổ cứng Enterprise NVMe PCIe 4.0 giúp kiểm soát tuyệt đối dung lượng, tối ưu hóa tốc độ đọc ngẫu nhiên (4K Random Read > 50.000 IOPS) và đơn giản hóa quy trình snapshot/backup ngoại vi.

Tạo cấu trúc thư mục lưu trữ tách biệt cho từng thành phần dữ liệu stateful:

sudo mkdir -p /opt/dify/docker/volumes/app/storage \
              /opt/dify/docker/volumes/db/data \
              /opt/dify/docker/volumes/redis/data \
              /opt/dify/docker/volumes/weaviate \
              /opt/dify/docker/volumes/sandbox/dependencies

# Phân quyền chính xác theo UID/GID của từng tiến trình container
# PostgreSQL container mặc định chạy dưới UID 999:999 (hoặc 70:70 tùy base image)
sudo chown -R 999:999 /opt/dify/docker/volumes/db/data
sudo chmod 700 /opt/dify/docker/volumes/db/data

# Redis container chạy dưới UID 999:999
sudo chown -R 999:999 /opt/dify/docker/volumes/redis/data
sudo chmod 750 /opt/dify/docker/volumes/redis/data

# Dify API/Worker container chạy dưới UID 1001:1001 (hoặc non-root appuser)
sudo chown -R 1001:1001 /opt/dify/docker/volumes/app/storage
sudo chmod 775 /opt/dify/docker/volumes/app/storage

Nếu bỏ qua bước thiết lập UID/GID này, PostgreSQL sẽ từ chối khởi động với lỗi FATAL: data directory "/var/lib/postgresql/data" has wrong ownership, trong khi tiến trình API sẽ ném ngoại lệ PermissionDenied khi xử lý tác vụ tải tài liệu PDF/DOCX lên kho lưu trữ RAG.

Cấu hình tệp biến môi trường .env chuẩn Production

Dify cung cấp tệp mẫu .env.example. Sao chép sang .env và tiến hành sinh các chuỗi mã hóa bảo mật ngẫu nhiên:

cp .env.example .env

Khởi tạo các chuỗi khóa bí mật bắt buộc bằng lệnh openssl từ terminal:

# Tạo khóa mã hóa phiên và cookie cho API và Web Console
SECRET_KEY=$(openssl rand -base64 42)
DB_PASSWORD=$(openssl rand -hex 16)
REDIS_PASSWORD=$(openssl rand -hex 16)

# Cập nhật trực tiếp các biến cốt lõi vào file .env
sed -i "s|^SECRET_KEY=.*|SECRET_KEY=${SECRET_KEY}|" .env
sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=${DB_PASSWORD}|" .env
sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=${REDIS_PASSWORD}|" .env

Mở tệp .env bằng nano hoặc vim để rà soát và cấu hình các khối thông số kỹ thuật cốt lõi sau:

# --- BẢO MẬT & DOMAIN TRUY CẬP ---
CONSOLE_API_URL=http://your-server-ip/console/api
CONSOLE_WEB_URL=http://your-server-ip
SERVICE_API_URL=http://your-server-ip/api
APP_API_URL=http://your-server-ip/api
FILES_URL=http://your-server-ip/files

# Khóa bí mật đã sinh tự động
SECRET_KEY=C2h4ZXJ0eXVpb3BhZHNkZmdoamtsbXp4Y3Zibm1xd2VydHl1aW8=

# --- CƠ SỞ DỮ LIỆU POSTGRESQL ---
DB_USERNAME=postgres
DB_PASSWORD=generated_secure_db_password_here
DB_HOST=db
DB_PORT=5432
DB_DATABASE=dify

# --- BỘ NHỚ ĐỆM & HÀNG ĐỢI CELERY (REDIS) ---
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=generated_secure_redis_password_here
REDIS_DB=0
REDIS_USE_SSL=false

# --- CẤU HÌNH LƯU TRỮ TỆP TIN (STORAGE) ---
# Sử dụng 'local' cho phân vùng gắn kết bền vững trên NVMe
STORAGE_TYPE=local
STORAGE_LOCAL_PATH=/app/api/storage

# --- VECTOR DATABASE ENGINE (WEAVIATE HOẶC QDRANT) ---
VECTOR_STORE=weaviate
WEAVIATE_ENDPOINT=http://weaviate:8080
WEAVIATE_API_KEY=

# --- GIỚI HẠN THỰC THI & TIMEOUT TRÊN WORKER ---
# Đảm bảo các tác vụ vector hóa tài liệu lớn không bị ngắt quãng
CELERY_WORKER_CONCURRENCY=4
CELERY_TASK_TIME_LIMIT=7200
CELERY_TASK_SOFT_TIME_LIMIT=3600

# --- PHÒNG CHỐNG TẤN CÔNG SSRF ---
SSRF_PROXY_HTTP_URL=http://ssrf_proxy:3128
SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128
ENABLE_SSRF_PROXY=true

Cần chú ý tham số CELERY_WORKER_CONCURRENCY. Giá trị này nên được thiết lập bằng đúng số vCPU vật lý được phân bổ cho máy chủ. Trên hạ tầng ảo hóa KVM chuẩn của tropic.host, hệ số CPU Steal Time (%st) luôn được khóa cứng ở mức 0.0% nhờ chính sách phân bổ vCPU độc lập không overcommit. Điều này đảm bảo khi Celery worker phân tích các mô hình embedding kích thước lớn, tiến trình tính toán vector không bị hypervisor bóp nghẽn hoặc delay chu kỳ xung nhịp.

Cấu hình docker-compose.yaml với giới hạn tài nguyên và phân vùng bền vững

Mặc định, tệp docker-compose.yaml của Dify không cấu hình deploy.resources.limits và logging driver. Nếu không kiểm soát, các container xử lý dữ liệu nặng như Celery Worker hoặc PostgreSQL có thể kích hoạt cơ chế Linux OOM (Out Of Memory) Killer, làm sập toàn bộ hệ điều hành máy chủ.

Mở tệp docker-compose.yaml và áp dụng các thông số hạn mức cgroups v2, luân chuyển log và ánh xạ phân vùng bền vững:

version: '3.8'

x-logging: &default-logging
  driver: "json-file"
  options:
    max-size: "50m"
    max-file: "3"

services:
  # --- API BACKEND ENGINE ---
  api:
    image: langgenius/dify-api:0.15.3
    restart: always
    environment:
      - SCRIPT_NAME=/api
    env_file:
      - .env
    volumes:
      - /opt/dify/docker/volumes/app/storage:/app/api/storage
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2048M
    logging: *default-logging

  # --- ASYNCHRONOUS CELERY WORKER ---
  worker:
    image: langgenius/dify-api:0.15.3
    restart: always
    environment:
      - SCRIPT_NAME=/api
      - MODE=worker
    env_file:
      - .env
    volumes:
      - /opt/dify/docker/volumes/app/storage:/app/api/storage
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 3072M
    logging: *default-logging

  # --- WEB CONSOLE FRONTEND ---
  web:
    image: langgenius/dify-web:0.15.3
    restart: always
    env_file:
      - .env
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1024M
    logging: *default-logging

  # --- POSTGRESQL + PGVECTOR ---
  db:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_USER: ${DB_USERNAME:-postgres}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ${DB_DATABASE:-dify}
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - /opt/dify/docker/volumes/db/data:/var/lib/postgresql/data/pgdata
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME:-postgres} -d ${DB_DATABASE:-dify}"]
      interval: 5s
      timeout: 5s
      retries: 5
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2048M
    logging: *default-logging

  # --- REDIS IN-MEMORY ENGINE ---
  redis:
    image: redis:7-alpine
    restart: always
    command: >
      redis-server 
      --requirepass ${REDIS_PASSWORD} 
      --appendonly yes 
      --maxmemory 1024mb 
      --maxmemory-policy volatile-lru
    volumes:
      - /opt/dify/docker/volumes/redis/data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1280M
    logging: *default-logging

  # --- VECTOR STORE: WEAVIATE ---
  weaviate:
    image: semitechnologies/weaviate:1.19.0
    restart: always
    volumes:
      - /opt/dify/docker/volumes/weaviate:/var/lib/weaviate
    environment:
      QUERY_DEFAULTS_LIMIT: 25
      AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
      PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
      DEFAULT_VECTORIZER_MODULE: 'none'
      CLUSTER_HOSTNAME: 'node1'
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 3072M
    logging: *default-logging

  # --- REVERSE PROXY NGINX GATEWAY ---
  nginx:
    image: nginx:alpine
    restart: always
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
    depends_on:
      - api
      - web
    logging: *default-logging

Lệnh khai báo command bên trong dịch vụ redis áp dụng chính sách --maxmemory-policy volatile-lru nhằm bảo vệ bộ nhớ RAM: khi đạt ngưỡng 1024MB, Redis sẽ tự động dọn dẹp các cache keys hết hạn thay vì từ chối nhận thêm tin nhắn đẩy từ Celery.

Kéo Image và Khởi Chạy Cụm Container

Trước khi đưa dịch vụ lên online, cần tải toàn bộ Docker images về ổ cứng. Thao tác này giúp phát hiện sớm các lỗi nghẽn băng thông hoặc lỗi xác thực registry:

# Kéo toàn bộ Docker images của cụm dịch vụ
docker compose pull

Khởi chạy cụm dịch vụ chạy ngầm dưới chế độ daemon:

docker compose up -d

Giám sát quá trình khởi tạo container và tự động chạy cơ chế database migration của PostgreSQL:

# Kiểm tra trạng thái healthcheck và runtime của các container
docker compose ps

Đầu ra chuẩn phải hiển thị tất cả các container đều ở trạng thái Up (đặc biệt db và redis phải có cờ (healthy)):

NAME                IMAGE                               COMMAND                  SERVICE             CREATED             STATUS                    PORTS
docker-api-1        langgenius/dify-api:0.15.3          "/bin/bash /entrypoi…"   api                 45 seconds ago      Up 44 seconds             5001/tcp
docker-db-1         postgres:15-alpine                  "docker-entrypoint.s…"   db                  45 seconds ago      Up 44 seconds (healthy)   5432/tcp
docker-nginx-1      nginx:alpine                        "/docker-entrypoint.…"   nginx               45 seconds ago      Up 44 seconds             0.0.0.0:80->80/tcp
docker-redis-1      redis:7-alpine                      "docker-entrypoint.s…"   redis               45 seconds ago      Up 44 seconds (healthy)   6379/tcp
docker-weaviate-1   semitechnologies/weaviate:1.19.0    "/bin/weaviate --hos…"   weaviate            45 seconds ago      Up 44 seconds             8080/tcp
docker-web-1        langgenius/dify-web:0.15.3          "/bin/sh ./entrypoin…"   web                 45 seconds ago      Up 44 seconds             3000/tcp
docker-worker-1     langgenius/dify-api:0.15.3          "/bin/bash /entrypoi…"   worker              45 seconds ago      Up 44 seconds             5001/tcp

Kiểm tra nhật ký khởi tạo backend để xác nhận các bảng cơ sở dữ liệu đã được tự động tạo lập thành công:

docker compose logs -f api | grep -E "Upgrade database|Running migrations"

Khi đầu ra trả về các thông điệp di trú schema INFO [alembic.runtime.migration] Context impl PostgresqlImpl và hoàn tất chuỗi migration mà không phát sinh cờ lỗi DB Connection Refused, hệ thống microservices Dify AI đã hoạt động ổn định trên phân vùng dữ liệu bền vững. Lúc này cổng HTTP 80 đã sẵn sàng phục vụ các yêu cầu thiết lập tài khoản quản trị đầu tiên.

Cấu Hình Nginx Reverse Proxy Với Chứng Chỉ SSL Let's Encrypt

Khi triển khai và cài dify ai trên vps docker trong môi trường production, việc để container docker-nginx-1 mở trực tiếp cổng HTTP 80 ra Internet tiềm ẩn rủi ro lớn về an toàn thông tin: toàn bộ API key (OpenAI, Anthropic), vector embeddings và dữ liệu người dùng đều truyền tải dưới dạng văn bản thuần (plaintext). Đồng thời, cấu hình mặc định của Nginx trong Docker Compose bị giới hạn kích thước upload body (client_max_body_size 1m) và bật chế độ đệm proxy (proxy_buffering on), khiến các tác vụ upload tài liệu RAG dung lượng lớn lập tức bị chặn với mã lỗi HTTP 413 Request Entity Too Large, còn quá trình truyền luồng token (Server-Sent Events - SSE) từ các mô hình ngôn ngữ lớn (LLM) bị giật lag nghiêm trọng do các chunk phản hồi bị giữ lại trong buffer.

Mô hình triển khai chuẩn cho hệ thống production là cô lập cổng của Dify về loopback interface nội bộ (127.0.0.1), sau đó dựng một Nginx Reverse Proxy độc lập trên máy chủ host (hoặc cấu hình Nginx Edge) kết hợp chứng chỉ TLS 1.3 từ Let's Encrypt, kích hoạt giao thức HTTP/2, mở rộng bộ đệm timeout cho LLM reasoning và nâng trần dung lượng tải tài liệu lên 150MB.


1. Cô lập cổng Dify về Loopback Interface (127.0.0.1)

Trước khi cấu hình Nginx trên host, cần giải phóng cổng 80 và 443 công khai của VPS để tránh xung đột socket (bind: address already in use).

Truy cập vào thư mục dify/docker/, chỉnh sửa file môi trường .env để chuyển cổng phơi bày của container Nginx sang cổng nội bộ 8080:

cd /opt/dify/docker
sed -i 's/^EXPOSE_NGINX_PORT=.*/EXPOSE_NGINX_PORT=8080/' .env
sed -i 's/^EXPOSE_NGINX_SSL_PORT=.*/EXPOSE_NGINX_SSL_PORT=8443/' .env

Nếu trong file docker-compose.yaml cổng được gán tĩnh theo định dạng 0.0.0.0:80:80, hãy sửa trực tiếp block nginx để chỉ lắng nghe trên 127.0.0.1:

  nginx:
    image: nginx:alpine
    restart: always
    ports:
      - "127.0.0.1:8080:80"

Tái khởi động cụm microservices để áp dụng thay đổi mạng:

docker compose down
docker compose up -d

Kiểm tra trạng thái socket mạng trên kernel Linux bằng ss:

ss -tulpn | grep -E ':(80|443|8080)'

Đầu ra bắt buộc phải hiển thị 127.0.0.1:8080 do tiến trình docker-proxy nắm giữ, trong khi cổng 0.0.0.0:80 và 0.0.0.0:443 phải hoàn toàn giải phóng.


2. Cài đặt Nginx Host và Cấp Chứng Chỉ SSL Let's Encrypt

Cài đặt Nginx phiên bản mới nhất cùng công cụ Certbot và plugin Nginx trên hệ điều hành Ubuntu 24.04/Debian 12:

apt-get update
apt-get install -y nginx certbot python3-certbot-nginx openssl

Khởi tạo nhóm tham số Diffie-Hellman 2048-bit để tăng cường tính bí mật chuyển tiếp hoàn hảo (Perfect Forward Secrecy - PFS) cho các phiên bắt tay TLS:

openssl dhparam -out /etc/nginx/dhparam.pem 2048

Trỏ bản ghi DNS A của tên miền (ví dụ: dify.yourdomain.com) về địa chỉ IPv4 tĩnh chuyên dụng của máy chủ VPS. Tại nền tảng đám mây tropic.host, mỗi cụm máy chủ ảo KVM NVMe đều được cấp phát IPv4 tĩnh sạch, không bị dính blacklist thư rác và không bị giới hạn cổng outbound/inbound, đảm bảo quá trình xác thực tên miền qua ACME Challenge của Let's Encrypt diễn ra tức thì.

Thực hiện xin chứng chỉ SSL thông qua cơ chế độc lập (Standalone) hoặc thông qua Certbot Nginx:

certbot certonly --nginx \
  -d dify.yourdomain.com \
  --agree-tos \
  --no-eff-email \
  --email [email protected]

Certbot sẽ lưu trữ cặp khóa tại: - Chứng chỉ công khai: /etc/letsencrypt/live/dify.yourdomain.com/fullchain.pem - Khóa riêng tư: /etc/letsencrypt/live/dify.yourdomain.com/privkey.pem


3. Thiết kế cấu hình Nginx Reverse Proxy tối ưu cho RAG và SSE

Tạo file cấu hình virtual host cho Dify tại /etc/nginx/sites-available/dify.conf:

cat << 'EOF' > /etc/nginx/sites-available/dify.conf
# Giới hạn tốc độ kết nối bảo vệ endpoint xác thực
limit_req_zone $binary_remote_addr zone=dify_auth_limit:10m rate=5r/s;

# Upstream trỏ về Dify Web/Nginx nội bộ
upstream dify_backend {
    server 127.0.0.1:8080;
    keepalive 64;
}

# Redirect toàn bộ HTTP sang HTTPS (301 Permanent)
server {
    listen 80;
    listen [::]:80;
    server_name dify.yourdomain.com;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

# Máy chủ HTTPS chính thức
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name dify.yourdomain.com;

    # Cấu hình chứng chỉ SSL/TLS
    ssl_certificate /etc/letsencrypt/live/dify.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dify.yourdomain.com/privkey.pem;
    ssl_dhparam /etc/nginx/dhparam.pem;

    # Tối ưu hóa giao thức và Cipher Suites (Chuẩn Mozilla Modern)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # TLS Session Cache tối ưu thời gian bắt tay (Handshake)
    ssl_session_timeout 1d;
    ssl_session_cache shared:DifySSL:10m;
    ssl_session_tickets off;

    # Kích hoạt OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 8.8.8.8 valid=300s;
    resolver_timeout 5s;

    # Header bảo mật hệ thống
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # ========================================================
    # TỐI ƯU HÓA UPLOAD TÀI LIỆU RAG & BUFFER CỦA INGESTION PIPE
    # ========================================================
    # Cho phép tải các tập tin PDF, DOCX, CSV phục vụ Knowledge Base lên đến 150MB
    client_max_body_size 150M;
    client_body_buffer_size 1M;
    client_body_timeout 300s;
    client_header_timeout 60s;

    # Đường dẫn bộ nhớ đệm tạm trên đĩa NVMe tốc độ cao
    client_body_temp_path /var/spool/nginx/client_temp 1 2;

    # Nhật ký truy cập và lỗi định dạng JSON
    access_log /var/log/nginx/dify_access.log combined;
    error_log /var/log/nginx/dify_error.log warn;

    # Bảo vệ chống brute-force endpoint đăng nhập
    location ~* ^/(console/api/login|api/login) {
        limit_req zone=dify_auth_limit burst=10 nodelay;
        proxy_pass http://dify_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # ========================================================
    # TỐI ƯU HÓA REVERSE PROXY CHO LLM STREAMING & WEBSOCKETS
    # ========================================================
    location / {
        proxy_pass http://dify_backend;
        proxy_http_version 1.1;

        # Hỗ trợ nâng cấp giao thức WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Chuyển tiếp định danh Client nguyên bản
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # TẮT TOÀN BỘ BUFFERING ĐỂ STREAMING TOKEN THỜI GIAN THỰC (SSE)
        proxy_buffering off;
        proxy_cache off;
        chunked_transfer_encoding on;

        # NÂNG TIMEOUT CHO CÁC MÔ HÌNH REASONING VÀ TÁC VỤ INGESTION LỚN
        # Tránh lỗi 504 Gateway Timeout khi Claude/DeepSeek-R1 xử lý context dài
        proxy_connect_timeout 600s;
        proxy_send_timeout 3600s;
        proxy_read_timeout 3600s;

        # TCP Socket Tuning
        tcp_nodelay on;
        tcp_nopush off;
    }
}
EOF

Khi người dùng đồng thời tải lên nhiều tài liệu RAG có dung lượng hàng trăm megabyte, Nginx sẽ ghi tạm dữ liệu xuống phân vùng client_body_temp_path. Trên các máy chủ đám mây ảo hóa KVM của tropic.host, việc trang bị ổ cứng NVMe PCIe 4.0 chuẩn Enterprise với hiệu năng đọc ghi ngẫu nhiên 4K QD1 vượt mức 50,000 IOPS giúp quá trình spooling dữ liệu diễn ra gần như tức thì, triệt tiêu hoàn toàn độ trễ I/O Wait (thông số %st = 0.0% được giữ vững tuyệt đối trên CPU AMD EPYC / Ryzen 9), không gây nghẽn tiến trình của worker Celery đang phân tích vector ở tầng dưới.


4. Tinh chỉnh Kernel Network Stack (sysctl) Cho Nginx

Để Nginx xử lý mượt mà hàng nghìn kết nối đồng thời từ các client gọi API Dify và duy trì luồng SSE ổn định, cần tối ưu hóa bảng socket của hệ điều hành Linux tại /etc/sysctl.d/99-network-tuning.conf:

cat << 'EOF' > /etc/sysctl.d/99-network-tuning.conf
# Tăng kích thước hàng đợi backlog cho socket
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 8192

# Kích hoạt tái sử dụng cổng TCP TIME_WAIT
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# Tối ưu hóa kích thước cửa sổ nhận/gửi TCP
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# Giữ kết nối TCP sống động và vô hiệu hóa slow start sau thời gian nhàn rỗi
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5
EOF

sysctl --system

Cấu hình net.ipv4.tcp_slow_start_after_idle = 0 kết hợp thuật toán điều khiển tắc nghẽn TCP BBR (được tích hợp sẵn trên hạ tầng mạng 1–10 Gbps của tropic.host) loại bỏ việc giảm tốc độ phát gói tin sau khi luồng kết nối tạm dừng, đảm bảo độ trễ phản hồi từng token (p99 latency) của giao diện chat Dify luôn đạt mức tối ưu dưới 20ms đối với các điểm kết nối IXP Frankfurt, Amsterdam và Singapore.


5. Kiểm tra cú pháp và Kích hoạt Site

Khởi tạo thư mục đệm tạm thời cho Nginx và gán quyền sở hữu chính xác cho người dùng www-data:

mkdir -p /var/spool/nginx/client_temp
chown -R www-data:www-data /var/spool/nginx/client_temp
chmod 700 /var/spool/nginx/client_temp

Xóa cấu hình mặc định (default site), kích hoạt file cấu hình mới của Dify và kiểm tra cú pháp Nginx:

rm -f /etc/nginx/sites-enabled/default
ln -sf /etc/nginx/sites-available/dify.conf /etc/nginx/sites-enabled/
nginx -t

Nếu hệ thống trả về thông báo:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Thực hiện nạp lại tiến trình Nginx để áp dụng cấu hình:

systemctl restart nginx
systemctl enable nginx

6. Thiết lập Tự động Gia hạn Chứng chỉ SSL

Chứng chỉ Let's Encrypt có thời hạn 90 ngày. Cần đảm bảo certbot.timer của systemd đang hoạt động và cấu hình deploy-hook tự động tải lại Nginx ngay sau khi gia hạn thành công:

mkdir -p /etc/letsencrypt/renewal-hooks/deploy
cat << 'EOF' > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/bash
systemctl reload nginx
EOF

chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Kiểm tra cơ chế tự động gia hạn bằng lệnh mô phỏng (dry-run):

certbot renew --dry-run

Khi kết quả trả về Congratulations, all simulated renewals succeeded, chu trình bảo mật SSL đã sẵn sàng vận hành tự động dài hạn mà không đòi hỏi can thiệp thủ công.


7. Nghiệm thu Kết nối và Kiểm tra Giới hạn Payload

Thực hiện truy vấn kiểm tra trực tiếp từ terminal máy trạm để xác thực chuyển hướng HTTP 301, phiên bản giao thức HTTP/2 và tiêu đề bảo mật HSTS:

curl -Iv https://dify.yourdomain.com

Đầu ra phản hồi phải thể hiện rõ:

HTTP/2 200 
server: nginx
date: Sun, 04 Oct 2026 14:10:00 GMT
content-type: text/html; charset=utf-8
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN

Kiểm tra khả năng tải tập tin dung lượng lớn bằng cách đẩy một payload giả lập 100MB qua lệnh curl:

dd if=/dev/zero of=/tmp/test_100mb.bin bs=1M count=100
curl -k -F "file=@/tmp/test_100mb.bin" https://dify.yourdomain.com/console/api/files/upload -w "%{http_code}\n" -o /dev/null -s
rm -f /tmp/test_100mb.bin

Mã HTTP trả về không được xuất hiện 413. Lúc này, hệ thống Dify AI đã được bảo vệ toàn diện sau lớp giáp Nginx Reverse Proxy, sẵn sàng cho bước tạo tài khoản quản trị viên và thiết lập pipeline RAG tài liệu phức tạp.

Kết Nối Mô Hình Ngôn Ngữ Lớn và Tích Hợp Cơ Sở Dữ Liệu Vector RAG

Sau khi lớp reverse proxy Nginx và chứng chỉ SSL hoàn tất kiểm tra tải trọng payload, giao diện web của Dify đã sẵn sàng cho giai đoạn khởi tạo tài khoản quản trị và thiết lập pipeline RAG (Retrieval-Augmented Generation). Trong quá trình cài dify ai trên vps docker, bước liên kết mô hình ngôn ngữ lớn (LLM) và tối ưu hóa cơ sở dữ liệu vector là trọng tâm quyết định trực tiếp tới tốc độ sinh token (Time to First Token - TTFT) và độ chính xác của ngữ cảnh truy xuất.


1. Khởi tạo Không gian làm việc và Thiết lập Kết nối Outbound

Truy cập địa chỉ https://dify.yourdomain.com/install trên trình duyệt để khởi tạo tài khoản Administrator cấp cao nhất. Dify yêu cầu thiết lập email quản trị, mật khẩu phức tạp (tối thiểu 8 ký tự bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt) và đặt tên cho Workspace đầu tiên.

Ngay khi hoàn tất bước khởi tạo, toàn bộ các tác vụ gọi mô hình suy luận hoặc embedding sẽ do hai container docker-api-1 và docker-worker-1 thực hiện dưới vai trò HTTP outbound client. Để đảm bảo các luồng streaming dữ liệu qua Server-Sent Events (SSE) không bị ngắt quãng, hạ tầng mạng của VPS phải có bảng định tuyến sạch, phân giải DNS nội bộ tối ưu và không bị drop gói tin TCP.

Kiểm tra trực tiếp độ trễ và tính khả dụng của kết nối mạng từ bên trong container dify-api tới các endpoint API quốc tế:

docker exec -it docker-api-1 curl -o /dev/null -s -w 'Total Time: %{time_total}s | DNS Lookup: %{time_namelookup}s | Connect: %{time_connect}s\n' https://api.openai.com/v1/models

Chỉ số time_total phản ánh độ trễ mạng vật lý từ máy chủ tới cụm server của nhà cung cấp model. Khi vận hành trên hạ tầng KVM của tropic.host, đường truyền uplink 1–10 Gbps kích hoạt sẵn thuật toán điều khiển tắc nghẽn TCP BBR cùng kết nối BGP peering trực tiếp tại các trung tâm trung chuyển lưu lượng lớn (Frankfurt, Amsterdam, Singapore) giúp triệt tiêu hiện tượng jitter, duy trì time_total ở mức tối thiểu và loại bỏ rủi ro nghẽn cổ chai mạng khi client nhận stream token thời gian thực.


2. Cấu hình Model Provider: OpenAI và DeepSeek API

Dify quản lý danh mục mô hình tập trung tại mục Settings > Model Provider. Hệ thống hỗ trợ tích hợp trực tiếp qua khóa API chính thức hoặc thông qua các cổng tương thích giao thức OpenAI (OpenAI-compatible).

┌────────────────────────────────────────────────────────┐
│                   Dify API / Worker                    │
└───────────────┬────────────────────────┬───────────────┘
                │                        │
       (HTTP / SSE Stream)      (HTTP / SSE Stream)
                │                        │
                ▼                        ▼
┌───────────────────────────────┐ ┌──────────────────────┐
│       OpenAI Endpoints        │ │  DeepSeek Endpoints  │
│  - gpt-4o                     │ │  - deepseek-chat     │
│  - text-embedding-3-large     │ │  - deepseek-reasoner │
└───────────────────────────────┘ └──────────────────────┘

Tích hợp OpenAI Native

  1. Điều hướng tới Settings > Model Provider > OpenAI.
  2. Nhấp Set API Key, nhập API Key (dạng sk-proj-...).
  3. Khai báo danh mục mô hình cần kích hoạt:
  4. LLM: gpt-4o, gpt-4o-mini phục vụ các tác vụ tổng hợp thông tin và phân loại intent.
  5. Text Embedding: text-embedding-3-large (kích thước vector 3072 chiều) hoặc text-embedding-3-small (1536 chiều) để vector hóa tài liệu.

Tích hợp DeepSeek qua Giao thức Tương thích OpenAI

DeepSeek-V3 (deepseek-chat) và DeepSeek-R1 (deepseek-reasoner) mang lại hiệu năng suy luận xuất sắc với chi phí token tối ưu. Do kiến trúc suy luận dạng Reasoning (Chain-of-Thought) của DeepSeek-R1 có thể kéo dài thời gian phản hồi trước khi trả về token đầu tiên, hệ thống cần được cấu hình timeout phù hợp.

  1. Tại danh sách Model Provider, chọn OpenAI-API-compatible và nhấp Add Model.
  2. Thiết lập các trường tham số:
  3. Model Type: LLM
  4. Model Name: deepseek-chat (hoặc deepseek-reasoner)
  5. Server URL: https://api.deepseek.com
  6. API Key: Khóa API cấp từ DeepSeek Platform (sk-...)
  7. Hiệu chỉnh Timeout trên Worker:
    Khi DeepSeek-R1 giải quyết các bài toán logic phức tạp, thời gian suy luận nội tại có thể vượt quá 60 giây. Mặc định, reverse proxy hoặc worker của Dify có thể ngắt kết nối nếu không nhận được dữ liệu phản hồi. Truy cập thư mục cấu hình Dify và bổ sung biến môi trường vào tệp .env:
cd /opt/dify/docker
nano .env

Bổ sung hoặc hiệu chỉnh các giá trị timeout kết nối HTTP:

# Tăng thời gian chờ phản hồi từ các reasoning model quy mô lớn
HTTP_REQUEST_NODE_MAX_BINARY_SIZE=104857600
CODE_EXECUTION_ENDPOINT_TIMEOUT=300
SSRF_PROXY_HTTP_TIMEOUT=180

Tải lại cấu hình container để biến môi trường có hiệu lực:

docker compose up -d

3. Cơ chế Lưu trữ Vector và Tối ưu I/O Phần cứng

Trong kiến trúc mặc định khi cài dify ai trên vps docker, công cụ tìm kiếm vector mặc định là Weaviate (docker-weaviate-1). Weaviate chịu trách nhiệm lưu trữ cấu trúc đồ thị HNSW (Hierarchical Navigable Small World) của các vector nhúng và bảng chỉ mục ngược (Inverted Index) cho tìm kiếm từ khóa BM25.

Tài liệu thô (PDF/DOCX)
       │
       ▼
[Text Splitter] ─────────► Chunks (500-800 tokens, Overlap 10%)
                                 │
                                 ▼
                    [Embedding: text-embedding-3-small]
                                 │
                                 ▼
                     Vector Array (Float32, 1536 dims)
                                 │
                                 ▼
┌────────────────────────────────────────────────────────┐
│                   Weaviate Vector DB                   │
│  ┌──────────────────────────┐┌──────────────────────┐  │
│  │ HNSW Graph (Memory/Disk) ││ BM25 Inverted Index  │  │
│  └──────────────────────────┘└──────────────────────┘  │
└────────────────────────────────────────────────────────┘

Quá trình nạp dữ liệu (indexing) vào Vector DB tạo ra hai điểm nghẽn tài nguyên chính: 1. CPU Spikes: Khi worker tính toán chia nhỏ văn bản và tính toán khoảng cách cosine/dot-product giữa các node đồ thị HNSW. 2. Random 4K Disk I/O: Khi Weaviate flush các commit log và cập nhật liên tục các tệp vector nhị phân trên phân vùng /var/lib/weaviate.

Nếu máy chủ bị tranh chấp tài nguyên CPU (CPU Steal Time %st > 0), các worker Celery xử lý embedding sẽ rơi vào trạng thái nghẽn hàng đợi (queue starvation), dẫn đến việc job import dữ liệu bị treo ở trạng thái Queued hoặc Indexing 0%.

Hạ tầng ảo hóa KVM chuẩn chỉ của tropic.host đảm bảo tỷ lệ overcommit tài nguyên bằng 0, duy trì thông số %st = 0.0% tuyệt đối trên nền tảng vi xử lý AMD EPYC và Ryzen 9. Đồng thời, hệ thống ổ cứng Enterprise NVMe PCIe 4.0 cung cấp tốc độ đọc/ghi ngẫu nhiên 4K QD1 vượt ngưỡng 50,000 IOPS, xử lý triệt để các đợt ghi dữ liệu dồn dập của cơ sở dữ liệu vector mà không làm tăng thời gian chờ I/O (%iowait luôn dưới 0.5%).


4. Xây dựng Kiến thức RAG (Dataset Indexing Pipeline)

Để kiểm chứng toàn bộ chuỗi xử lý từ trích xuất văn bản, sinh vector nhúng đến lưu trữ chỉ mục, tiến hành tạo một Dataset mới:

  1. Trên menu Dify, truy cập tab Knowledge > nhấp Create Knowledge.
  2. Tải lên tệp tài liệu kỹ thuật mẫu (định dạng PDF hoặc Markdown, dung lượng < 15MB).
  3. Cấu hình Phân đoạn Văn bản (Text Chunking):
  4. Chọn chế độ Custom.
  5. Segment Identifier: \n\n (ngắt theo đoạn văn logic để bảo toàn ngữ cảnh).
  6. Maximum Chunk Length: Thiết lập 600 đến 800 tokens. Kích thước này tối ưu hóa dung lượng ngữ cảnh đưa vào prompt của LLM mà không làm loãng trọng tâm câu trả lời.
  7. Chunk Overlap: Thiết lập 10% (khoảng 60 đến 80 tokens) để các chunk liền kề không bị đứt đoạn thông tin ngữ nghĩa tại biên giới phân tách.
  8. Lựa chọn Chỉ mục và Mô hình Nhúng (Index Method & Embedding):
  9. Indexing Technique: Chọn High Quality.
  10. Embedding Model: Chọn text-embedding-3-small (OpenAI).
  11. Cấu hình Chiến lược Truy xuất (Retrieval Setting):
  12. Kích hoạt Hybrid Search (Tìm kiếm kết hợp): Thuật toán kết hợp đồng thời Vector Search (tìm kiếm theo khoảng cách ngữ nghĩa HNSW) và Full-Text Search (tìm kiếm chính xác từ khóa qua BM25).
  13. Reranking Model: Nếu dự án yêu cầu độ chính xác cao đối với các tài liệu chuyên ngành, tích hợp thêm mô hình Cohere Rerank (rerank-v3.5) hoặc BGE-Reranker. Đặt ngưỡng điểm liên quan (Score Threshold) ở mức 0.65 và giới hạn Top K trả về ở mức 3 đến 5 đoạn trích phù hợp nhất.
  14. Nhấp Save & Process để đẩy tác vụ vào hàng đợi Celery.

5. Giám sát Hàng đợi Xử lý và Tối ưu Hạt nhân Linux

Khi bấm xử lý tài liệu, Dify đưa tác vụ vào hàng đợi Redis để container dify-worker nhận việc. Theo dõi trạng thái hoạt động thực tế của worker thông qua terminal của VPS:

docker exec -it docker-worker-1 celery -A app.celery inspect active

Nếu worker đang bận rộn xử lý embedding, đầu ra sẽ hiển thị chi tiết task document_indexing:

{
  "celery@dify-worker": [
    {
      "id": "c7a8b1c4-92e1-45a9-b3a6-89d4e5f2a1b7",
      "name": "tasks.document_indexing_task.document_indexing_task",
      "args": ["dataset_id_uuid", "document_id_uuid"],
      "kwargs": {},
      "type": "tasks.document_indexing_task.document_indexing_task",
      "time_start": 1728051000.123
    }
  ]
}

Kiểm tra độ ổn định tài nguyên và sự tương tác giữa CPU, RAM và Disk I/O trong thời gian index:

vmstat 1 5

Phân tích đầu ra vmstat: * Cột r (runnable processes) không được vượt quá số lượng vCPU được cấp phát. * Cột wa (I/O wait) phải tiệm cận mức 0. Nếu wa > 5, ổ cứng đang bị nghẽn do tốc độ IOPS thấp, làm chậm toàn bộ pipeline RAG. * Cột st (steal time) bắt buộc phải là 0. Nếu st > 0, node ảo hóa đang bị chia sẻ tài nguyên quá mức từ nhà cung cấp VPS.

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0      0 3145620  84520 2154300    0    0    12   240  450  820 18  4 78  0  0

Để ngăn chặn lỗi tràn bộ nhớ bảng băm ảo khi cơ sở dữ liệu vector Weaviate mở rộng quy mô bộ nhớ ảo hóa (memory-mapped files - mmap), điều chỉnh tham số hạt nhân Linux trên host:

sudo sysctl -w vm.max_map_count=262144
sudo sysctl -w fs.file-max=2097152
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.d/99-dify.conf
echo "fs.file-max=2097152" | sudo tee -a /etc/sysctl.d/99-dify.conf
sudo sysctl -p /etc/sysctl.d/99-dify.conf

Khi trạng thái tài liệu trên giao diện Dify Knowledge chuyển sang màu xanh lá cây (Available), pipeline RAG đã sẵn sàng. Bạn có thể sử dụng tính năng Hit Testing trực tiếp trong giao diện Dataset để gõ thử câu hỏi truy vấn, kiểm tra điểm số tương đồng cosine của các chunk và xác nhận ngữ cảnh trích xuất khớp hoàn toàn với dữ liệu gốc trước khi bước sang giai đoạn cấu hình ứng dụng Chatbot hoặc Workflow.

Quy Trình Sao Lưu Tự Động và Khôi Phục Thảm Họa (Disaster Recovery)

Một kiến trúc RAG doanh nghiệp không thể coi là hoàn thiện nếu thiếu chiến lược bảo vệ dữ liệu trạng thái (stateful data). Khi triển khai và vận hành hệ thống cài Dify AI trên VPS Docker trong môi trường production, dữ liệu không chỉ nằm ở một cơ sở dữ liệu duy nhất mà phân tán trên ba thành phần cốt lõi:

  1. PostgreSQL: Chứa toàn bộ metadata ứng dụng, tài khoản người dùng, cấu hình pipeline RAG, nhật ký hội thoại (chat logs), API keys và trạng thái tác vụ Celery.
  2. Vector Database (Milvus Standalone): Chứa các vector embeddings của tài liệu, cấu trúc đồ thị chỉ mục HNSW/IVF_FLAT. Milvus lưu trữ siêu dữ liệu (metadata) trên etcd và toàn bộ vector data segments trên MinIO.
  3. MinIO / Local Storage Volume: Lưu trữ tài liệu thô ban đầu (PDF, DOCX, Markdown), avatar và các tệp đính kèm do người dùng tải lên.

Việc sao lưu bằng cách snapshot toàn bộ ổ đĩa (raw volume snapshot) khi các container đang chạy dễ dẫn đến tình trạng hỏng cấu trúc B-Tree của PostgreSQL hoặc phân mảnh vector segment của Milvus do các dirty pages chưa kịp xả (flush) từ bộ nhớ đệm RAM xuống đĩa. Dưới đây là quy trình sao lưu cấp ứng dụng (application-level consistent backup) và kịch bản phục hồi thảm họa chi tiết.


1. Kiến Trúc Sao Lưu Nhất Quán (Application-Consistent Backup)

Quy trình sao lưu tự động phải kích hoạt lệnh đóng băng/xả dữ liệu từ RAM xuống đĩa, sau đó trích xuất dưới dạng nén và mã hóa bất đối xứng trước khi đẩy lên máy chủ lưu trữ offsite.

┌────────────────────────────────────────────────────────────────────────┐
│                   Dify AI Host (Docker Engine)                         │
│                                                                        │
│  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────────┐  │
│  │ PostgreSQL Cont. │  │   etcd / Milvus  │  │     MinIO Cont.      │  │
│  │ (Transaction Log)│  │ (Metadata State) │  │  (Raw Document S3)  │  │
│  └────────┬─────────┘  └────────┬─────────┘  └──────────┬───────────┘  │
│           │ pg_dump             │ etcdctl snapshot      │ mc mirror    │
│           ▼                     ▼                       ▼              │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │  Tập lệnh backup: /opt/dify-ops/scripts/backup.sh                │  │
│  │  - Nén zstd đa luồng (-T0)                                       │  │
│  │  - Mã hóa GPG đối xứng (AES-256)                                 │  │
│  │  - Giới hạn I/O: ionice -c2 -n7 & nice -n 19                     │  │
│  └──────────────────────────────────┬───────────────────────────────┘  │
└─────────────────────────────────────┼──────────────────────────────────┘
                                      │ rclone copy (TCP BBR 1-10 Gbps)
                                      ▼
             ┌─────────────────────────────────────────────────┐
             │ Offsite Storage / Remote Backup Node (S3 / SFTP)│
             └─────────────────────────────────────────────────┘

Tối ưu I/O khi sao lưu trên hạ tầng VPS

Quá trình pg_dump và nén dữ liệu đọc tuần tự hàng gigabyte dữ liệu sẽ chiếm dụng tài nguyên I/O nghiêm trọng. Trên các nền tảng VPS giá rẻ bị overcommit, thao tác này khiến thời gian trễ đĩa tăng vọt, đẩy %wa (I/O Wait) lên trên 40% và kích hoạt lỗi Gateway Timeout (504) trên Nginx của Dify.

Hạ tầng KVM VPS tại tropic.host giải quyết triệt để rủi ro này nhờ ổ cứng Enterprise NVMe PCIe 4.0 với tốc độ đọc ngẫu nhiên 4K QD1 đạt trên 50,000 IOPS và độ trễ ghi p99 dưới 1.5ms. Kết hợp cùng cam kết ảo hóa thuần túy KVM không chia sẻ tài nguyên CPU (CPU Steal Time %st = 0.0%), tiến trình backup nền được cô lập hoàn toàn, không làm suy giảm hiệu năng xử lý truy vấn RAG theo thời gian thực.


2. Triển Khai Kịch Bản Sao Lưu Tự Động (backup.sh)

Tạo thư mục vận hành và cấp quyền cô lập cho script:

sudo mkdir -p /opt/dify-ops/{scripts,backups,keys}
sudo chmod 700 /opt/dify-ops

Tạo file chứa mật khẩu mã hóa GPG an toàn:

openssl rand -base64 32 | sudo tee /opt/dify-ops/keys/backup.pass > /dev/null
sudo chmod 400 /opt/dify-ops/keys/backup.pass

Khởi tạo script sao lưu toàn diện /opt/dify-ops/scripts/backup.sh:

#!/usr/bin/env bash
set -Eeuo pipefail

# --- CẤU HÌNH HỆ THỐNG ---
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
DIFY_DIR="/opt/dify/docker"
BACKUP_ROOT="/opt/dify-ops/backups"
TARGET_DIR="${BACKUP_ROOT}/${TIMESTAMP}"
PASS_FILE="/opt/dify-ops/keys/backup.pass"
RETENTION_DAYS=7

# Cấu hình container names (theo docker-compose của Dify)
CONTAINER_PG="docker-db-1"
CONTAINER_ETCD="docker-etcd-1"
CONTAINER_MINIO="docker-minio-1"
PG_USER="postgres"
PG_DB="dify"

mkdir -p "${TARGET_DIR}"

log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] $*"
}

error_exit() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] [ERROR] $*" >&2
    exit 1
}

# 1. SAO LƯU POSTGRESQL (Atomic Snapshot qua Custom Format)
log "Khởi tạo sao lưu PostgreSQL (${PG_DB})..."
docker exec -t "${CONTAINER_PG}" pg_dump \
    -U "${PG_USER}" \
    -d "${PG_DB}" \
    -F c \
    -b \
    -v \
    -f "/tmp/pg_dump_${TIMESTAMP}.dump" || error_exit "Lỗi pg_dump!"

docker cp "${CONTAINER_PG}:/tmp/pg_dump_${TIMESTAMP}.dump" "${TARGET_DIR}/postgres_${TIMESTAMP}.dump"
docker exec "${CONTAINER_PG}" rm -f "/tmp/pg_dump_${TIMESTAMP}.dump"

# 2. SAO LƯU METADATA MILVUS (etcd Snapshot)
log "Sao lưu etcd snapshot (Milvus vector metadata)..."
docker exec -e ETCDCTL_API=3 "${CONTAINER_ETCD}" etcdctl \
    --endpoints=127.0.0.1:2379 \
    snapshot save "/tmp/etcd_snapshot_${TIMESTAMP}.db" || error_exit "Lỗi etcdctl snapshot!"

docker cp "${CONTAINER_ETCD}:/tmp/etcd_snapshot_${TIMESTAMP}.db" "${TARGET_DIR}/etcd_${TIMESTAMP}.db"
docker exec "${CONTAINER_ETCD}" rm -f "/tmp/etcd_snapshot_${TIMESTAMP}.db"

# 3. SAO LƯU ĐỒNG BỘ DỮ LIỆU TÀI LIỆU VÀ CHUNK SEGMENT (MinIO Data)
log "Đóng gói dữ liệu MinIO..."
MINIO_VOLUME_SRC=$(docker volume inspect docker_minio_data --format '{{ .Mountpoint }}' 2>/dev/null || echo "")

if [ -d "${MINIO_VOLUME_SRC}" ]; then
    # Sử dụng tar kết hợp cgroups/ionice để không gây nghẽn I/O
    tar --warning=no-file-changed -czf "${TARGET_DIR}/minio_data_${TIMESTAMP}.tar.gz" -C "${MINIO_VOLUME_SRC}" . || true
else
    log "Volume docker_minio_data không tìm thấy qua Docker Socket, kiểm tra thư mục cục bộ..."
    if [ -d "${DIFY_DIR}/volumes/app/storage" ]; then
        tar -czf "${TARGET_DIR}/local_storage_${TIMESTAMP}.tar.gz" -C "${DIFY_DIR}/volumes/app/storage" .
    fi
fi

# 4. SAO LƯU BIẾN MÔI TRƯỜNG VÀ CẤU HÌNH COMPOSE
log "Sao lưu file cấu hình hệ thống (.env, docker-compose.yaml)..."
cp "${DIFY_DIR}/.env" "${TARGET_DIR}/dify.env"
cp "${DIFY_DIR}/docker-compose.yaml" "${TARGET_DIR}/docker-compose.yaml"

# 5. NÉN TOÀN BỘ BẢN GHI, TÍNH CHECKSUM VÀ MÃ HÓA GPG
log "Mã hóa và đóng gói gói sao lưu tổng hợp..."
tar -C "${BACKUP_ROOT}" -cf - "${TIMESTAMP}" | \
    gpg --batch --yes --passphrase-file "${PASS_FILE}" \
        --symmetric --cipher-algo AES256 \
        -o "${BACKUP_ROOT}/dify_backup_${TIMESTAMP}.tar.gpg"

# Tạo mã SHA256 để xác thực tính toàn vẹn khi phục hồi
sha256sum "${BACKUP_ROOT}/dify_backup_${TIMESTAMP}.tar.gpg" > "${BACKUP_ROOT}/dify_backup_${TIMESTAMP}.tar.gpg.sha256"

# Dọn dẹp thư mục thô chưa mã hóa
rm -rf "${TARGET_DIR}"

# 6. ĐẨY LÊN OFFSITE STORAGE QUA RCLONE (Tùy chọn)
if command -v rclone &> /dev/null; then
    log "Đang đồng bộ bản sao lưu ra Remote Storage..."
    rclone copy "${BACKUP_ROOT}/dify_backup_${TIMESTAMP}.tar.gpg" remote-backup:dify-backups/ \
        --bwlimit 50M \
        --s3-upload-concurrency 4
fi

# 7. CHÍNH SÁCH GIỮ LẠI BẢN GHI (RETENTION POLICY)
log "Thực thi dọn dẹp các bản sao lưu cũ hơn ${RETENTION_DAYS} ngày..."
find "${BACKUP_ROOT}" -type f -name "dify_backup_*.tar.gpg*" -mtime +"${RETENTION_DAYS}" -delete

log "Sao lưu hoàn tất thành công: ${BACKUP_ROOT}/dify_backup_${TIMESTAMP}.tar.gpg"

Phân quyền thực thi:

sudo chmod 700 /opt/dify-ops/scripts/backup.sh

Thiết lập Cron Job định kỳ với độ ưu tiên I/O thấp

Cấu hình cron job chạy vào 02:30 sáng hàng ngày. Kích hoạt nice và ionice để ép tiến trình chạy ở class idle/best-effort thấp nhất, ngăn ngừa nghẽn hàng đợi đĩa:

sudo crontab -e

Thêm chỉ thị sau:

30 2 * * * nice -n 19 ionice -c2 -n7 /opt/dify-ops/scripts/backup.sh >> /var/log/dify-backup.log 2>&1

3. Kịch Bản Khôi Phục Thảm Họa (Disaster Recovery Runbook)

Kịch bản này xử lý tình huống máy chủ VPS gốc gặp sự cố phần cứng, hỏng tệp tin hệ thống hoặc bị tấn công xóa trắng dữ liệu. Mục tiêu: phục hồi toàn bộ cụm Dify hoạt động bình thường trên một máy chủ KVM VPS mới tinh.

Bước 1: Chuẩn bị máy chủ mới và môi trường Docker

Trên máy chủ VPS mới (ví dụ instance Ubuntu 24.04 LTS trên tropic.host):

# Cập nhật hệ thống và cài đặt Docker Engine chính thức
sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg zstd
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Cấu hình kernel sysctl bắt buộc cho database và vector search
sudo tee /etc/sysctl.d/99-dify-dr.conf << 'EOF'
vm.max_map_count=262144
fs.file-max=2097152
net.core.somaxconn=1024
EOF
sudo sysctl -p /etc/sysctl.d/99-dify-dr.conf

Bước 2: Tải bản sao lưu, giải mã và xác thực tính toàn vẹn

Đưa file sao lưu, file sha256 và key giải mã lên máy chủ mới (đặt tại /tmp):

cd /tmp

# Kiểm tra mã băm bảo toàn dữ liệu
sha256sum -c dify_backup_*.tar.gpg.sha256

# Giải mã tệp lưu trữ bằng file key bảo mật
gpg --batch --yes --passphrase-file /opt/dify-ops/keys/backup.pass \
    --decrypt -o dify_restored.tar dify_backup_*.tar.gpg

# Giải nén nội dung bản sao lưu
mkdir -p /tmp/restore_payload
tar -xf dify_restored.tar -C /tmp/restore_payload
RESTORE_DIR=$(find /tmp/restore_payload -mindepth 1 -maxdepth 1 -type d)
echo "Thư mục khôi phục: ${RESTORE_DIR}"

Bước 3: Tái tạo cấu hình và khởi tạo các dịch vụ Stateful

Triển khai lại cấu hình Dify:

sudo mkdir -p /opt/dify/docker
sudo cp "${RESTORE_DIR}/dify.env" /opt/dify/docker/.env
sudo cp "${RESTORE_DIR}/docker-compose.yaml" /opt/dify/docker/docker-compose.yaml

cd /opt/dify/docker

# Khởi chạy duy nhất các cụm lưu trữ để chuẩn bị nạp dữ liệu
docker compose up -d db etcd minio

Chờ khoảng 15 giây để PostgreSQL và etcd hoàn thành chu kỳ khởi động tiến trình con (probe socket ready):

docker compose exec -T db pg_isready -U postgres -d dify

Bước 4: Khôi phục cơ sở dữ liệu PostgreSQL

Tiến hành nạp lại schema và toàn bộ bảng dữ liệu vào database dify:

RESTORE_DUMP=$(find "${RESTORE_DIR}" -name "postgres_*.dump")

# Chép tệp dump vào container
docker cp "${RESTORE_DUMP}" docker-db-1:/tmp/restore.dump

# Thực hiện pg_restore với cờ dọn dẹp các bảng mặc định vừa sinh ra
docker compose exec -T db pg_restore \
    -U postgres \
    -d dify \
    --clean \
    --if-exists \
    --no-owner \
    --no-privileges \
    -v /tmp/restore.dump

# Dọn dẹp tệp tạm trong container
docker compose exec -T db rm -f /tmp/restore.dump

Bước 5: Khôi phục etcd Metadata (Milvus Vector Store)

Dừng container etcd để thay thế dữ liệu cluster state mà không bị xung đột khóa:

docker compose stop etcd

RESTORE_ETCD=$(find "${RESTORE_DIR}" -name "etcd_*.db")
ETCD_DATA_DIR=$(docker volume inspect docker_etcd_data --format '{{ .Mountpoint }}')

# Xóa dữ liệu etcd khởi tạo ban đầu và phục hồi từ snapshot
sudo rm -rf "${ETCD_DATA_DIR:?}"/*

docker run --rm \
    -v "${RESTORE_ETCD}:/backup.db" \
    -v "${ETCD_DATA_DIR}:/etcd_data" \
    -e ETCDCTL_API=3 \
    quay.io/coreos/etcd:v3.5.5 \
    etcdctl snapshot restore /backup.db \
    --data-dir=/etcd_data

# Khởi động lại etcd
docker compose start etcd

Bước 6: Khôi phục MinIO Object Storage

Phục hồi toàn bộ tệp tài liệu, ảnh, tệp nhúng raw vector segments:

MINIO_DATA_DIR=$(docker volume inspect docker_minio_data --format '{{ .Mountpoint }}')
MINIO_TAR=$(find "${RESTORE_DIR}" -name "minio_data_*.tar.gz")

if [ -f "${MINIO_TAR}" ]; then
    docker compose stop minio
    sudo rm -rf "${MINIO_DATA_DIR:?}"/*
    sudo tar -xzf "${MINIO_TAR}" -C "${MINIO_DATA_DIR}"
    docker compose start minio
fi

Bước 7: Khởi động toàn bộ cụm Dify và kiểm thử chức năng

Khi toàn bộ dữ liệu trạng thái đã được đưa về trạng thái đồng nhất, khởi động tất cả các container còn lại (Worker, API, Web, Plugin Daemon, Milvus, Redis, Nginx):

docker compose up -d

Kiểm tra trạng thái hoạt động của các container:

docker compose ps

Toàn bộ các container phải hiển thị trạng thái Up (hoặc healthy).

Thực thi kiểm tra tính toàn vẹn (Sanity Check) trực tiếp từ terminal host:

# 1. Kiểm tra API backend Dify phản hồi mã 200
curl -I -s http://127.0.0.1/api/healthz | head -n 1

# 2. Kiểm tra số lượng bản ghi tài liệu đã được phục hồi trong PostgreSQL
docker compose exec -T db psql -U postgres -d dify -c "SELECT count(*) FROM documents;"

# 3. Kiểm tra số lượng vector collections trong Milvus
docker compose exec -T milvus-standalone curl -s http://localhost:9091/api/v1/collections

Nếu số lượng bản ghi trong bảng documents khớp với thống kê trước sự cố và cổng API trả về HTTP/1.1 200 OK, quá trình phục hồi thảm họa đã hoàn tất thành công. Hệ sinh thái Dify AI đã sẵn sàng tiếp tục phục vụ các tác vụ inference và trích xuất ngữ cảnh RAG mà không làm mất mát bất kỳ tài liệu hay lịch sử hội thoại nào của người dùng.

Câu hỏi thường gặp (FAQ)

Cần cấu hình VPS tối thiểu bao nhiêu để chạy Dify AI?

Để chạy đầy đủ các microservice của Dify (gồm PostgreSQL, Redis, Weaviate/Milvus và Dify Server), bạn cần tối thiểu VPS KVM 4 vCPU và 8 GB RAM.

Tại sao nên tự host Dify trên VPS thay vì dùng bản Cloud?

Tự host trên VPS KVM giúp doanh nghiệp bảo vệ 100% dữ liệu tài liệu nội bộ, không bị giới hạn số lượng token/workflow và tiết kiệm chi phí hàng tháng.