How to Install Dify on Ubuntu 24.04 — Self-Hosted LLMOps Platform
Dify is an open-source LLMOps platform that bundles agent orchestration, RAG, prompt management, dataset ingestion, and an API gateway behind a clean web UI. Self-hosting Dify on your own VPS gives you a private control plane for building AI apps — chatbots, agents, workflows, and knowledge-base assistants — without sending a single prompt or document to a shared cloud. This guide walks you through a production-grade install on Ubuntu 24.04 using Docker Compose, Nginx with Let's Encrypt, and an Ollama-backed local model provider.
Prefer a managed install path? Our Professional VPS plan at EUR 19.99/month is sized for Dify plus a local Ollama 8B model on one box. Bring the domain and follow this guide end to end in about 45 minutes.
Table of Contents
What is Dify?
Dify is an all-in-one LLMOps platform that lets you design, ship, and operate AI applications without writing boilerplate around every provider and vector database. In a single deployment it gives you:
- A visual agent and workflow builder with nodes for LLM calls, tools, conditional branches, loops, and HTTP requests. The same canvas that powers tools like Flowise or n8n, but purpose-built for LLMs.
- A prompt IDE with versioning, A/B testing, variable management, and model-agnostic templates.
- A RAG pipeline for document ingestion, chunking, embedding, and hybrid retrieval against a vector store.
- A model provider abstraction supporting OpenAI, Anthropic, Azure OpenAI, Google Vertex AI, AWS Bedrock, Hugging Face, Ollama, vLLM, and dozens of others — all behind one OpenAI-compatible API.
- A dataset and conversation log viewer that captures every prompt, response, latency, cost estimate, and token count so you can debug and optimize over time.
- A hosted API gateway that turns every app you build into a
/v1/chat-messagesendpoint you can call from your own product backend.
Code node in workflows.The official documentation lives at docs.dify.ai.
Why Self-Host Dify Instead of Using Dify Cloud?
Dify Cloud is a great way to try the product in ten minutes, but serious use of an LLMOps platform quickly runs into the limits of any multi-tenant SaaS:
- Data residency and privacy. Everything you upload to a Knowledge base — contracts, support tickets, internal wikis — is embedded and stored. On your own VPS that data never leaves the box. On Dify Cloud it sits in shared infrastructure subject to that provider's policies.
- No per-message pricing. Dify Cloud charges by conversation messages and document storage. A self-hosted install is capped only by your VPS bill. On the Professional plan at EUR 19.99/month you get unlimited apps, unlimited API calls, and unlimited conversations.
- Access to private model endpoints. Dify Cloud cannot reach an Ollama server running on your LAN or an internal vLLM cluster. Self-hosted Dify sits next to them and can proxy requests with sub-millisecond network overhead.
- Custom tools and plugins. The self-hosted build lets you install community plugins, register custom tools that hit your internal APIs, and mount the
/storagevolume for arbitrary file access inside workflows. - Observability integration. Pair self-hosted Dify with Langfuse for trace-level LLM observability, or ship logs to your existing ELK/Loki stack. Cloud deployments are locked to the built-in log viewer.
- Air-gapped and regulated environments. Healthcare, finance, legal, and government workloads frequently cannot use any multi-tenant LLM platform. Self-hosted Dify on an EU VPS solves this cleanly.
Cost comparison at 50k messages/month
| Item | Dify Cloud (Team) | Self-Hosted on CloudCore Professional |
|---|---|---|
| Platform fee | ~$59/month | EUR 19.99/month VPS |
| Message overage | Metered | None |
| Documents | Capped | Disk-bound |
| Private Ollama models | Not supported | Fully supported |
| Custom domain | Paid add-on | Included |
| Langfuse integration | Limited | Unlimited |
| Team seats | Per-seat | Unlimited |
Prerequisites
- An Ubuntu 24.04 LTS VPS with root or sudo access. We use the CloudCore Professional plan (4 vCPU, 8 GB RAM, 100 GB NVMe SSD, EUR 19.99/month) throughout this guide.
- A registered domain name with an
Arecord pointing at your VPS public IP (for exampledify.example.com). - Open ports 22 (SSH), 80, and 443 on the firewall.
- A local terminal capable of SSH. On macOS/Linux use the built-in terminal; on Windows use Windows Terminal or PuTTY.
- Basic familiarity with
vim/nanofor editing configuration files.
ssh root@your-server-ipStep 1: Prepare the Ubuntu 24.04 Server
Update the package index and installed packages so dependency resolution is clean:
sudo apt update && sudo apt upgrade -yInstall a few helpers we will use later:
sudo apt install -y ca-certificates curl gnupg lsb-release git ufwEnable the UFW firewall with sane defaults:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verboseIf the kernel was updated by the upgrade, reboot once before proceeding:
sudo rebootStep 2: Install Docker and Docker Compose
Dify is distributed as a Docker Compose stack, so we install Docker Engine plus the Compose v2 plugin from the official Docker apt repository — not from apt install docker.io, which lags behind.
Add Docker's GPG key and repository:
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 $(lsb_release -cs) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Install Docker Engine, CLI, containerd, Buildx, and the Compose plugin:
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginEnable and start the daemon:
sudo systemctl enable --now dockerVerify:
docker --version
docker compose versionExpected output:
Docker version 27.3.1, build ce12230
Docker Compose version v2.29.7Optionally add your user to the docker group so you can skip sudo:
sudo usermod -aG docker $USER
newgrp dockerStep 3: Clone the Dify Repository
All production deployment files live in the docker/ subdirectory of the main Dify repository.
cd /opt
sudo git clone https://github.com/langgenius/dify.git
sudo chown -R $USER:$USER /opt/dify
cd /opt/dify/dockerPin to the latest stable release tag rather than main — see the Dify release notes for the current version:
git fetch --tags
git checkout $(git describe --tags $(git rev-list --tags --max-count=1))List the files you now have:
ls -lYou should see docker-compose.yaml, .env.example, an nginx/ directory (Dify's internal nginx), volumes/, and supporting scripts.
Step 4: Configure the .env File
Copy the example file and open it for editing:
cp .env.example .env
vim .envAt minimum, change these values:
# ---------- Core ----------
Generate with: openssl rand -base64 42
SECRET_KEY=REPLACE_WITH_openssl_rand_base64_42_OUTPUTPublic URL (what users will type in the browser)
CONSOLE_API_URL=https://dify.example.com
CONSOLE_WEB_URL=https://dify.example.com
SERVICE_API_URL=https://dify.example.com
APP_API_URL=https://dify.example.com
APP_WEB_URL=https://dify.example.com
FILES_URL=https://dify.example.com---------- Database ----------
DB_USERNAME=postgres
DB_PASSWORD=CHANGE_ME_strong_pg_password
DB_HOST=db
DB_PORT=5432
DB_DATABASE=dify---------- Redis ----------
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=CHANGE_ME_strong_redis_password---------- Vector store ----------
VECTOR_STORE=weaviate
WEAVIATE_ENDPOINT=http://weaviate:8080
WEAVIATE_API_KEY=CHANGE_ME_weaviate_key---------- Storage ----------
STORAGE_TYPE=local
STORAGE_LOCAL_PATH=storage---------- Optional: built-in LLM providers ----------
Leave empty and configure in the UI if you prefer.
OPENAI_API_KEY=
OPENAI_API_BASE=Ollama running on the host (same box)
OLLAMA_API_BASE_URL=http://host.docker.internal:11434Generate the SECRET_KEY and strong database passwords in another terminal:
openssl rand -base64 42
openssl rand -hex 24Paste them into the right variables in .env.
If you run Ollama on the same VPS, the Dify API container needshost.docker.internalto resolve to the host. The Dify compose file handles this on Linux by addingextra_hosts: ["host.docker.internal:host-gateway"]— verify it is present for theapiandworkerservices, or add it yourself.
If you prefer Qdrant as the vector store, set:
VECTOR_STORE=qdrant
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=CHANGE_ME_qdrant_key...and add a qdrant service to docker-compose.yaml or run Qdrant in a sibling stack.
Step 5: Start the Dify Stack
From /opt/dify/docker, pull the images and bring everything up:
docker compose pull
docker compose up -dFirst-time pull downloads several gigabytes of images (api, worker, web, nginx, PostgreSQL, Redis, Weaviate, Sandbox, SSRF proxy). Expect 2–5 minutes on a 1 Gbps link.
Check that all containers are healthy:
docker compose psExpected output:
NAME IMAGE STATUS PORTS
docker-api-1 langgenius/dify-api:0.15.0 Up 1 minute (healthy) 5001/tcp
docker-worker-1 langgenius/dify-api:0.15.0 Up 1 minute (healthy)
docker-web-1 langgenius/dify-web:0.15.0 Up 1 minute (healthy) 3000/tcp
docker-db-1 postgres:15-alpine Up 1 minute (healthy) 5432/tcp
docker-redis-1 redis:6-alpine Up 1 minute (healthy) 6379/tcp
docker-weaviate-1 semitechnologies/weaviate:1.19.0 Up 1 minute 8080/tcp
docker-sandbox-1 langgenius/dify-sandbox:0.2.10 Up 1 minute (healthy) 8194/tcp
docker-nginx-1 nginx:latest Up 1 minute 0.0.0.0:80->80/tcp
docker-ssrf_proxy-1 ubuntu/squid:latest Up 1 minute 3128/tcpThe internal nginx container already listens on port 80 of the host. Tail the API logs to confirm migrations finished:
docker compose logs -f apiYou want to see Running on http://0.0.0.0:5001 and no tracebacks. Press Ctrl+C to detach.
Step 6: Install Nginx and Issue an SSL Certificate
Dify's internal nginx container only speaks HTTP. In production we front it with a host-level Nginx + Let's Encrypt certificate so the public URL is https://dify.example.com.
First, stop Dify's published port 80 so host Nginx can bind it. Edit docker-compose.yaml and change the nginx service port mapping:
nginx:
ports:
- "127.0.0.1:8080:80"Recreate the container:
docker compose up -d nginxInstall host Nginx and Certbot:
sudo apt install -y nginx certbot python3-certbot-nginxCreate the site config:
sudo tee /etc/nginx/sites-available/dify > /dev/null <<'EOF' server { listen 80; server_name dify.example.com;client_max_body_size 100m;
location / { proxy_pass http://127.0.0.1:8080; 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;
# Workflow/agent streaming (SSE) proxy_buffering off; proxy_cache off; proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_http_version 1.1; proxy_set_header Connection ""; } } EOF
sudo ln -s /etc/nginx/sites-available/dify /etc/nginx/sites-enabled/ sudo rm -f /etc/nginx/sites-enabled/default sudo nginx -t && sudo systemctl reload nginx
Issue the certificate (replace the domain and email):
sudo certbot --nginx -d dify.example.com --redirect \
--agree-tos -m [email protected] --no-eff-emailCertbot edits the server block to add the SSL directives and an HTTP-to-HTTPS redirect. Verify auto-renewal is scheduled:
sudo systemctl list-timers | grep certbotVisit https://dify.example.com. You should see the Dify splash screen asking you to create an admin account.
Step 7: Complete the First-Run Setup
The first person to reach the install URL becomes the workspace owner. On the /install page:
You land on the workspace dashboard with tabs for Studio, Knowledge, Tools, and Explore. The URL is now https://dify.example.com/apps.
Step 8: Add an Ollama Model Provider
Dify needs at least one model provider to be useful. If you already have Ollama running on the same VPS, plug it in now.
Pull a model on the host if you have not already:
ollama pull llama3.1:8b
ollama pull nomic-embed-textMake sure Ollama listens on all interfaces so the Dify container can reach it:
sudo mkdir -p /etc/systemd/system/ollama.service.d
sudo tee /etc/systemd/system/ollama.service.d/override.conf > /dev/null <<EOF
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
EOF
sudo systemctl daemon-reload
sudo systemctl restart ollamaIn the Dify UI:
llama3.1:8b.http://host.docker.internal:11434.Chat, context size to 8192, and max tokens to 4096.Repeat for the embedding model:
- Model Name:
nomic-embed-text - Base URL:
http://host.docker.internal:11434 - Model Type:
Text Embedding - Dimensions:
768
gpt-4o-mini for tricky reasoning and Ollama llama3.1:8b for high-volume, low-latency tasks. See the Dify model providers documentation for per-provider fields.Step 9: Build Your First Agent Workflow
Time to prove the stack works end to end.
Server Docs Helper, pick an icon, click Create.llama3.1:8b and keep temperature around 0.3.You are a VPS support assistant for vps-server.host customers.
Answer concisely. If the user mentions a URL, use the Webscraper tool
to read it before replying. Refuse to answer questions unrelated to
hosting, Linux administration, or Dify.The agent should call the Current Time tool, then Webscraper, then compose an answer grounded in both results. If the tokens never start streaming, check docker compose logs -f api for a stack trace — the most common cause is the Ollama base URL not being reachable from the container.
For more complex flows with branches and loops, use Create from Blank → Chatflow or Workflow instead of Agent. Dify's workflow canvas is conceptually similar to Flowise but stores state in PostgreSQL and emits traces you can forward to Langfuse.
Step 10: Create a RAG Knowledge Base
RAG is where Dify shines for support and internal search use cases.
.docx documents (up to 15 MB each by default).nomic-embed-text if you configured it in Step 8, otherwise OpenAI text-embedding-3-small.Dify ships the documents to Celery workers that extract text, chunk it, embed each chunk, and write vectors to Weaviate. Progress shows in the Knowledge detail page.
To let an agent use the knowledge base:
Server Docs Helper agent.Ask a question whose answer exists in the documents. The agent will retrieve the relevant chunks, inject them into the prompt, and cite them in the Debug side panel. This is the same pattern powering tools like LibreChat and Open WebUI, but with an operator UI you can hand to non-engineers.
Step 11: Issue API Keys and Invite Your Team
Every Dify app automatically exposes a stable HTTP API. To let your product backend call it:
app-...).Call it from your backend:
curl -X POST 'https://dify.example.com/v1/chat-messages' \
-H 'Authorization: Bearer app-xxxxxxxxxxxxxxxxxxxx' \
-H 'Content-Type: application/json' \
-d '{
"inputs": {},
"query": "How do I check disk usage on Ubuntu?",
"response_mode": "streaming",
"user": "user-123"
}'The response streams Server-Sent Events identical in shape to OpenAI's streaming format, which is why we tuned Nginx for SSE in Step 6.
Team members and SSO
Click your avatar → Settings → Members → Invite. Enter email addresses and pick a role (Owner, Admin, Editor, Normal). Invitees receive an email with a signup link; they can authenticate with email/password, Google, or GitHub OAuth.
To enable OAuth, set these in .env and restart:
ENABLE_EMAIL_CODE_LOGIN=true
GOOGLE_CLIENT_ID=xxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxx
GITHUB_CLIENT_ID=xxxxxxxx
GITHUB_CLIENT_SECRET=xxxxxxxxEnterprise SAML/OIDC is a paid tier — see the Dify enterprise docs if that is a requirement. Most self-hosted teams enforce 2FA at the identity provider level instead.
Operations: Backups, Upgrades, and Monitoring
Nightly backup cron
sudo tee /usr/local/bin/dify-backup.sh > /dev/null <<'EOF' #!/bin/bash set -euo pipefail TS=$(date +%Y%m%d-%H%M%S) DEST=/var/backups/dify/$TS mkdir -p "$DEST"cd /opt/dify/docker docker compose exec -T db pg_dump -U postgres dify | gzip > "$DEST/dify.sql.gz" tar czf "$DEST/volumes.tar.gz" volumes/ find /var/backups/dify -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \; EOF
sudo chmod +x /usr/local/bin/dify-backup.sh echo "15 3 * root /usr/local/bin/dify-backup.sh" | sudo tee /etc/cron.d/dify-backup
Upgrading Dify
cd /opt/dify/docker
git fetch --tags
git checkout $(git describe --tags $(git rev-list --tags --max-count=1))
docker compose pull
docker compose up -d
docker compose logs -f api | head -n 100Migrations run automatically on API startup.
Monitoring
The healthy path is two-layered: container health checks (already defined in docker-compose.yaml) and external uptime monitoring. Point Uptime Kuma or your existing monitoring system at:
https://dify.example.com/— web UIhttps://dify.example.com/console/api/setup— API health endpoint
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
502 Bad Gateway on the domain | Host Nginx cannot reach port 8080 | Check docker compose ps nginx, ensure it listens on 127.0.0.1:8080, sudo nginx -t |
| Setup page says "Install has already been completed" but no user works | Partial first-run state | docker compose exec db psql -U postgres -d dify -c "DELETE FROM dify_setups;" then refresh |
Agent replies Model not available | Provider credentials wrong or Ollama unreachable | Settings → Model Provider → re-test. From inside the api container: docker compose exec api curl http://host.docker.internal:11434/api/tags |
| RAG retrieval returns nothing | Embedding model mismatch between index and query time | Recreate the knowledge base after changing the embedding model — vectors are not portable across dimensions |
413 Request Entity Too Large on upload | Host Nginx body limit | Raise client_max_body_size in the server block and sudo systemctl reload nginx |
| Workflow streams then stalls at ~60 seconds | Nginx idle timeout | Confirm proxy_read_timeout 3600s is set (Step 6) |
| PostgreSQL container restart loops | Old volume with incompatible version | Back up first, then docker compose down && docker volume rm docker_db_data && docker compose up -d |
sandbox container unhealthy | Seccomp profile blocked on the host kernel | docker compose logs sandbox; on very old kernels set SANDBOX_ENABLE=false in .env |
# Follow all logs
docker compose logs -fJust the API
docker compose logs -f apiTail the last 200 lines of the worker
docker compose logs --tail=200 workerExec into PostgreSQL
docker compose exec db psql -U postgres -d difyFAQ
What are the minimum hardware requirements for self-hosting Dify?
Dify's own services (API, worker, web, PostgreSQL, Redis, Weaviate, Sandbox) need at least 4 vCPU and 8 GB RAM. If you also run Ollama on the same server, plan for 12 GB RAM or more so the model fits alongside the stack. Disk usage starts around 10 GB and grows with documents and vector embeddings. The CloudCore Professional plan (4 vCPU, 8 GB RAM, 100 GB NVMe, EUR 19.99/month) is the sweet spot for Dify plus a single 7B–8B Ollama model.
Should I use Dify Cloud or self-host Dify?
Dify Cloud is the fastest way to try the product but it stores prompts, knowledge documents, and conversation history on shared infrastructure and charges per message. Self-hosting keeps sensitive documents on your VPS, removes per-message pricing, allows unlimited apps and API keys, and lets you connect private model endpoints like Ollama that Dify Cloud cannot reach. Any team that will process more than a few thousand messages per month, or handle any regulated data, should self-host.
Can Dify use Ollama models instead of OpenAI?
Yes. Dify ships with a first-class Ollama provider. Point it at http://host.docker.internal:11434 (or your Ollama host) and every installed Ollama model becomes selectable in agents, chatflows, and the RAG pipeline. You can also mix providers — for example, an OpenAI embedding model with a local Llama 3.1 for generation — or use Dify's built-in rate-limit-free round-robin across multiple local models.
Which vector store should I use with Dify?
The default docker-compose ships Weaviate, which works out of the box for small-to-medium datasets (under a few million vectors). For larger datasets or if you already operate one elsewhere, Dify supports Qdrant, Milvus, PGVector, Chroma, and Elasticsearch. Switch by setting VECTOR_STORE in .env. Qdrant is a popular choice for self-hosters who want a fast, single-binary Rust-based store with a clean admin UI.
How do I back up a self-hosted Dify deployment?
Back up three things: the PostgreSQL volume (pg_dump inside the db container), the vector store volume (Weaviate or Qdrant data directory), and the uploaded files volume at docker/volumes/app/storage. A nightly cron that tars these directories and pushes them to S3-compatible object storage is sufficient for most teams. The script in the Operations section above covers the first two.
Does Dify support SSO for team members?
The Dify community edition supports email/password and OAuth (Google, GitHub) out of the box. SAML and OIDC-based enterprise SSO are available in the enterprise edition. For most self-hosted teams, the built-in invite flow plus OAuth covers the common cases; you can enforce MFA at the identity provider level. Audit logs of member actions are available in Settings → Security.
How do I upgrade Dify to a newer version?
Pull the latest tags in the docker directory with git pull (or git checkout <tag>), review any changes to docker-compose.yaml and .env.example, then run docker compose pull followed by docker compose up -d. The API container automatically runs database migrations on startup. Always take a PostgreSQL and volume backup before a major version bump, and subscribe to the Dify releases feed to get breaking-change notes early.
Next Steps
- Put traces in front of a dashboard. Install Langfuse on the same VPS and forward Dify traces to it. You get per-call token counts, latency percentiles, and error rates per agent.
- Add a chat UI for end users. Dify's own app frontend is great internally, but for external customers you may want to embed LibreChat or Open WebUI and point them at Dify's API.
- Scale vector search. Swap Weaviate for Qdrant when your knowledge base crosses a few hundred thousand vectors, or when you need filterable payloads.
- Drop in an agent prototyping sandbox. Pair Dify with Flowise so non-engineers can prototype in Flowise, then productionize the winning flows as Dify apps with proper API keys and observability.
- Automate deployment. Commit your
.env(with secrets in a vault),docker-compose.yaml, and Nginx config to an Ansible or Terraform repo so spinning up a staging Dify is a single command.
Ready to deploy? The CloudCore Professional VPS gives you the headroom for Dify plus a local Ollama model on one server for EUR 19.99/month. Spin it up, point your domain at it, and follow this guide from the top.