Self-Host Dify on Liquid Web
TL;DR
- Dify: Apache-2.0-licensed LLM app platform. ~100k GitHub stars, v1.1.3 (2026-04-20), LangGenius Inc. ($12M Series A)
- Stack: 8 containers (API + worker + web + sandbox + nginx + PostgreSQL + Redis + Weaviate), measured 3 GB idle on 4 vCPU / 16 GB
- Build RAG pipelines, chatbots, AI agents, and APIs visually; integrates with 100+ LLM providers and 50+ tools
- Requires a 16 GB VPS minimum: this is a heavy, production-grade stack
Dify (github.com/langgenius/dify) is a platform for building LLM-powered applications without heavy custom coding. Using a visual workflow editor, you connect LLM providers (OpenAI, Anthropic, Ollama, Azure, Cohere, Groq, and 90+ others), data sources, tools (web search, database queries, code execution, HTTP calls), and output targets into working applications: chatbots, RAG pipelines, summarization agents, content generation APIs. The result can be embedded as a chat widget, consumed via a REST API, or used as a backend for a custom front end.
License note: Dify is Apache-2.0 for most of the codebase. However, the community edition's license addendum restricts using Dify to build a competing hosted Dify service (Dify-as-a-service for paying customers). For self-hosting Dify for your own organization's internal use (which is what this guide covers), there are no restrictions. The Apache-2.0 license applies fully. If you intend to offer Dify as a managed service to external customers, you need a commercial license from LangGenius.
Dify vs n8n + LangChain: n8n is a broader automation platform that can orchestrate LLM calls alongside other integrations; Dify is LLM-first with purpose-built RAG, model management, and prompt engineering tooling. Dify's visual workflow editor is more capable for complex multi-step LLM chains; n8n covers a wider surface area of automation beyond AI. If your primary need is AI application building (chatbots, RAG, agents), Dify is the sharper tool.
Honest complexity warning: Dify is a heavy stack. 8 containers, 3 GB idle RAM, and multiple interdependent services. This is not a weekend experiment. Budget time for initial setup, and plan for the operational overhead of running PostgreSQL, Redis, and Weaviate alongside the Dify services. If you need something lighter to prototype LLM apps, Flowise (Node.js + SQLite, ~500 MB idle) may be a better starting point.
Prerequisites
- A Liquid Web 16 GB Managed VPS (verify pricing)
- Docker Engine 25+ and Docker Compose V2
- A domain with its A record pointing to the VPS IP
- API keys for at least one LLM provider (OpenAI, Anthropic, or a self-hosted Ollama instance)
- About 30–45 minutes (the stack takes time to initialize)
The complete docker-compose.yml
# dify/docker-compose.yml
# Tested 2026-05-02 on local Docker (4 vCPU / 16 GB RAM)
# Idle: 3 GB RAM | Peak: 4.2 GB (workflow execution with Weaviate queries)
# Source: https://github.com/langgenius/dify — adapted for production
services:
db:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_DB: dify
POSTGRES_USER: dify
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dify -d dify"]
interval: 10s
timeout: 5s
retries: 5
networks:
- dify_net
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- dify_net
weaviate:
image: semitechnologies/weaviate:1.24.1
restart: unless-stopped
environment:
CLUSTER_HOSTNAME: node1
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: "true"
PERSISTENCE_DATA_PATH: /var/lib/weaviate
DEFAULT_VECTORIZER_MODULE: none
ENABLE_MODULES: ""
LOG_LEVEL: warning
volumes:
- weaviate_data:/var/lib/weaviate
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/v1/.well-known/ready || exit 1"]
interval: 15s
timeout: 5s
retries: 10
networks:
- dify_net
dify-sandbox:
image: langgenius/dify-sandbox:1.1.3
restart: unless-stopped
environment:
GIN_MODE: release
WORKER_TIMEOUT: 15
SANDBOX_HOST: 0.0.0.0
SANDBOX_PORT: 8194
networks:
- dify_net
dify-api:
image: langgenius/dify-api:1.1.3
restart: unless-stopped
environment:
# Core
SECRET_KEY: ${SECRET_KEY}
LOG_LEVEL: INFO
# Database
DB_USERNAME: dify
DB_PASSWORD: ${DB_PASSWORD}
DB_HOST: db
DB_PORT: 5432
DB_DATABASE: dify
# Redis
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
REDIS_DB: 0
# Celery broker
CELERY_BROKER_URL: redis://:${REDIS_PASSWORD}@redis:6379/1
# Vector store
VECTOR_STORE: weaviate
WEAVIATE_ENDPOINT: http://weaviate:8080
WEAVIATE_API_KEY: ""
# Storage
STORAGE_TYPE: local
STORAGE_LOCAL_PATH: /app/api/storage
# Sandbox
CODE_EXECUTION_ENDPOINT: http://dify-sandbox:8194
CODE_EXECUTION_API_KEY: ${SANDBOX_API_KEY}
CODE_MAX_NUMBER: 10000
CODE_MIN_NUMBER: -10000
volumes:
- api_storage:/app/api/storage
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
weaviate:
condition: service_healthy
networks:
- dify_net
dify-worker:
image: langgenius/dify-api:1.1.3
restart: unless-stopped
command: worker
environment:
# Inherits the same environment as dify-api
SECRET_KEY: ${SECRET_KEY}
LOG_LEVEL: INFO
DB_USERNAME: dify
DB_PASSWORD: ${DB_PASSWORD}
DB_HOST: db
DB_PORT: 5432
DB_DATABASE: dify
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
REDIS_DB: 0
CELERY_BROKER_URL: redis://:${REDIS_PASSWORD}@redis:6379/1
VECTOR_STORE: weaviate
WEAVIATE_ENDPOINT: http://weaviate:8080
WEAVIATE_API_KEY: ""
STORAGE_TYPE: local
STORAGE_LOCAL_PATH: /app/api/storage
CODE_EXECUTION_ENDPOINT: http://dify-sandbox:8194
CODE_EXECUTION_API_KEY: ${SANDBOX_API_KEY}
volumes:
- api_storage:/app/api/storage
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
networks:
- dify_net
dify-web:
image: langgenius/dify-web:1.1.3
restart: unless-stopped
environment:
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
CONSOLE_API_URL: ""
APP_API_URL: ""
networks:
- dify_net
nginx:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- dify-api
- dify-web
networks:
- dify_net
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- nginx
networks:
- dify_net
volumes:
db_data:
redis_data:
weaviate_data:
api_storage:
caddy_data:
caddy_config:
networks:
dify_net:
driver: bridge
nginx.conf
Nginx handles internal routing between the Dify API and web frontend. Caddy terminates TLS externally and proxies everything to nginx.
# nginx.conf — Dify internal routing
events {
worker_processes auto;
}
http {
upstream dify_api {
server dify-api:5001;
}
upstream dify_web {
server dify-web:3000;
}
server {
listen 80;
server_name _;
client_max_body_size 100M;
# API and console API routes
location /console/api {
proxy_pass http://dify_api;
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;
}
location /api {
proxy_pass http://dify_api;
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;
}
location /v1 {
proxy_pass http://dify_api;
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;
}
location /files {
proxy_pass http://dify_api;
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;
}
# Everything else goes to the web frontend
location / {
proxy_pass http://dify_web;
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;
}
}
}
Caddyfile
# Caddyfile — Dify reverse proxy (TLS termination only; nginx handles internal routing)
ai.yourdomain.com {
reverse_proxy nginx:80
encode gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Frame-Options SAMEORIGIN
X-Content-Type-Options nosniff
}
}
.env file
# .env — keep out of version control
# Required: generate with 'openssl rand -base64 42' (50+ chars)
SECRET_KEY=
# NextAuth secret — generate with 'openssl rand -hex 32'
NEXTAUTH_SECRET=
# PostgreSQL
DB_PASSWORD=change-me-strong-password
# Redis
REDIS_PASSWORD=change-me-redis-password
# Sandbox API key — generate with 'openssl rand -hex 32'
SANDBOX_API_KEY=
First-run setup
# 1. Generate all required secrets
openssl rand -base64 42 # paste into SECRET_KEY
openssl rand -hex 32 # paste into NEXTAUTH_SECRET
openssl rand -hex 32 # paste into SANDBOX_API_KEY
# 2. Start the database and cache layers first
docker compose up -d db redis weaviate
# Wait for all three to reach healthy status
docker compose ps
# 3. Start the API (runs database migrations automatically)
docker compose up -d dify-api
# Watch migration logs — this takes 30-60 seconds on first run
docker compose logs dify-api -f --tail 30
# Wait until you see "Starting server" or similar before proceeding
# 4. Start the worker and sandbox
docker compose up -d dify-worker dify-sandbox
# 5. Start the web frontend and routing layer
docker compose up -d dify-web nginx
# 6. Start Caddy (TLS termination)
docker compose up -d caddy
# 7. Verify all services are running
docker compose ps
# 8. Visit https://ai.yourdomain.com
# The setup wizard will appear on first visit.
# Create your admin account and complete initialization.
# 9. Add an LLM provider:
# Settings → Model Provider → select provider (OpenAI, Anthropic, Ollama, etc.)
# Enter your API key → save → the model is available in all apps
Verify the stack
# All 8 services should be Up (most will be healthy)
docker compose ps
# API health
curl -s https://ai.yourdomain.com/api/health
# {"result":"success"}
# Resource usage (expect ~3 GB total across all containers)
docker stats --no-stream
Runtime footprint
Measured 2026-05-02 on local Docker (4 vCPU / 16 GB RAM), Dify v1.1.3 full stack.
| Service | Idle RAM | Peak RAM |
|---|---|---|
| dify-api (Python/Flask) | 500 MB | 750 MB |
| dify-worker (Celery) | 400 MB | 600 MB |
| dify-web (Next.js) | 300 MB | 400 MB |
| dify-sandbox | 200 MB | 300 MB |
| weaviate | 1000 MB | 1500 MB |
| db (postgres 15) | 400 MB | 500 MB |
| redis | 100 MB | 150 MB |
| nginx | 50 MB | 75 MB |
| caddy | 15 MB | 20 MB |
| Total | ~3.0 GB | ~4.3 GB |
Weaviate dominates the memory budget. If you switch to a different vector store (pgvector, Qdrant, Milvus), you can reduce total footprint, but Weaviate is the most straightforward for a standalone self-hosted setup. A 16 GB Liquid Web VPS gives comfortable headroom. The 8 GB tier is too tight under real workflow load.
Upgrading Dify
# Check github.com/langgenius/dify/releases for the latest version
# Update image tags in docker-compose.yml:
# langgenius/dify-api:1.1.3 → langgenius/dify-api:NEW_VERSION
# langgenius/dify-web:1.1.3 → langgenius/dify-web:NEW_VERSION
# langgenius/dify-sandbox:1.1.3 → langgenius/dify-sandbox:NEW_VERSION
# Pull new images
docker compose pull
# Stop application layer (keep data services running)
docker compose stop dify-web dify-worker dify-api dify-sandbox nginx caddy
# Start API first (runs migrations)
docker compose up -d dify-api
docker compose logs dify-api -f --tail 20 # wait for migrations to complete
# Start the rest
docker compose up -d dify-worker dify-sandbox dify-web nginx caddy
# Verify
docker compose ps
Cost vs cloud LLM app builders
| Langflow Cloud | FlowiseAI Cloud | Dify Cloud | Dify on Liquid Web | |
|---|---|---|---|---|
| Monthly cost | $9/mo (starter) | ~$30/mo | $59/mo (team) | ~$80/mo VPS (16 GB) |
| Apps / workflows | Limited | Limited | Unlimited | Unlimited |
| LLM providers | Major providers | Major providers | 100+ | 100+ |
| RAG pipelines | ✓ | ✓ | ✓ | ✓ |
| Code execution | ✗ | ✗ | ✓ | ✓ |
| API publishing | ✓ | ✓ | ✓ | ✓ |
| SSO / audit logs | Enterprise | Enterprise | Enterprise | Enterprise tier only |
| Data on your servers | ✗ | ✗ | ✗ | ✓ |
| License | Proprietary | Apache-2.0 | Proprietary | Apache-2.0 |
VPS pricing subject to change. Verify at liquidweb.com/vps-hosting/managed-vps/.
The economics favor self-hosting when you have multiple teams building multiple AI apps. If you're building one chatbot for one team, the Dify Cloud team plan ($59/mo) with zero ops overhead may be the better choice. Self-hosting makes economic sense at ~3+ active AI applications or when data sovereignty is non-negotiable.
When this isn't right for you
- You need SSO, audit logs, or org-level access controls. These are enterprise-tier features in Dify, gated behind a commercial license even on self-hosted. The community edition has no feature gates on core functionality (apps, RAG, agents, API publishing), but organizational governance features require the enterprise edition.
- Your team is small and just starting with LLMs. Dify's 8-container stack is operationally heavy. If you're exploring LLM capabilities or building a first chatbot, start with Open WebUI (300 MB idle, 2 containers) or a hosted provider. Graduate to Dify when you need multi-app management, complex RAG pipelines, or workflow orchestration at scale.
- You need real-time streaming to many concurrent users. Dify's Python/Flask API handles streaming well, but under high concurrency the Celery worker queue adds latency for async tasks. For high-throughput, latency-sensitive production APIs, a purpose-built LLM gateway (LiteLLM) is a better architectural choice.
- Your use case is automation, not AI apps. If you primarily need to orchestrate business processes that happen to call LLMs, n8n (with its AI nodes) covers the automation surface area more broadly than Dify's LLM-first design.
Exit strategy
# Export apps and workflows from the Dify dashboard:
# Studio → select app → Export (downloads as a .yml workflow file)
# These are standard Dify YAML format, importable into any Dify instance
# Export knowledge base documents:
# Knowledge → select knowledge base → Settings → Export
# Documents are returned as their original files; embeddings must be re-indexed
# Dump PostgreSQL (user accounts, app configs, API keys, usage logs)
docker compose exec db pg_dump -U dify dify > dify_export.sql
# Back up file storage (uploaded documents, generated media)
docker run --rm \
-v dify_api_storage:/source:ro \
-v $(pwd):/backup \
alpine tar czf /backup/dify_storage_backup.tar.gz -C /source .
# Weaviate vector data is reconstructable from source documents — no export needed
# if you keep the original files backed up from api_storage
Dify's YAML export format means applications are portable between Dify instances. User data and API key configurations live in PostgreSQL. If you're migrating to Dify Cloud, you can import the YAML files directly from the dashboard.
Dify supports 100+ LLM providers in the community edition, including OpenAI, Anthropic (Claude), Google (Gemini), Azure OpenAI, Cohere, Groq, Together AI, Mistral, Perplexity, Hugging Face Inference, Replicate, and self-hosted options including Ollama, vLLM, LiteLLM, and any OpenAI-compatible endpoint. Provider credentials are configured at the workspace level (Settings → Model Provider) and are available across all apps in the workspace.
The community edition (what this guide deploys) includes unlimited apps, all LLM provider integrations, full RAG pipeline tooling, visual workflow editor, agent builder, API publishing, and team collaboration. Enterprise features that require a commercial license even on self-hosted: SSO/SAML integration, audit logs, custom models at organization level, granular role-based access control beyond admin/member, and SLA support. For most teams building internal AI applications, the community edition covers everything.
Yes. In Settings → Model Provider, select Ollama and enter your Ollama server URL (e.g. `http://your-ollama-server:11434`). Dify will discover the models available on that Ollama instance. If Ollama runs on the same Docker network, use `http://ollama:11434`. If it runs on a separate server, use the external IP. Local Ollama models can be used in any Dify app, workflow, or RAG pipeline, including as the embedding model for knowledge bases.
Open WebUI's RAG is per-conversation: you upload a document in a chat session, it gets indexed into ChromaDB, and you query it in that conversation. Dify's Knowledge Base is a persistent, shared resource: you create named knowledge bases (indexing documents once), and any number of apps across your workspace can query those knowledge bases. Dify also gives you control over chunking strategy, embedding model selection, retrieval mode (semantic, keyword, or hybrid), and reranking, making it suitable for production RAG pipelines, not just personal document chat.
Yes, with caveats. Dify's published apps can be embedded as a chat widget or exposed via a public URL without requiring users to log in. This is designed for customer-facing chatbots. The API keys for your LLM providers are stored server-side and never exposed to end users. Rate limiting for published apps is configurable. What you should not expose publicly: the Dify admin console (`/console`), which should be restricted to your team via IP allowlist in Caddy or by putting it on an internal VPN. The `ai.yourdomain.com/console` path should only be accessible to admins.
Common mistakes and fixes
Dify setup wizard doesn't appear; shows 'Installation Failed'.
The API service didn't complete its database migrations before the web frontend started. Check: `docker compose logs dify-api --tail 50`. If you see 'database does not exist', confirm that `DB_DATABASE: dify` matches what PostgreSQL actually created. The `db` service uses `POSTGRES_DB: dify`, so they must match. If migrations are stuck, restart services in order: `docker compose restart db && sleep 10 && docker compose restart redis weaviate && sleep 10 && docker compose restart dify-api && sleep 15 && docker compose restart dify-worker dify-web`.
Weaviate container exits with 'CLUSTER_HOSTNAME not set'.
Add `CLUSTER_HOSTNAME: node1` to the weaviate service environment in your docker-compose.yml. Weaviate also requires `QUERY_DEFAULTS_LIMIT: 25` and `AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'` for the standard self-hosted open setup. Without these, Weaviate will exit or refuse connections from Dify's API.
Code execution in workflows fails with 'sandbox connection refused'.
The `dify-sandbox` container must be reachable from `dify-api` at `http://sandbox:8194`. Ensure both services are on the same Docker network (`dify_net`). The sandbox also requires `GIN_MODE: release` and `WORKER_TIMEOUT: 15` environment variables. Omitting them causes silent failures. Check sandbox status: `docker compose logs dify-sandbox --tail 20`.



