Dify Cloud Professional costs $59/month: run limits, credit quotas, and data on their servers. For $21.24-$47.99 on a VPS, you get a self-hosted AI instance on a KVM server that you control via SSH.
This guide covers installation, production hardening (SSL, secrets, backups), and your first AI workflow. On a clean server, setup takes 20-30 minutes.
What You Need Before You Start
Pick the plan that matches your workload:
For Dify with external APIs only (OpenAI, Anthropic). Sufficient for the core Dify stack, but cannot run local AI models:
- Medium VPS
- From $21.24/mo, with annual billing
- Specs: 3 CPU / 4 GB RAM / 40 GB NVMe
For entry-level local AI. Runs Dify combined with lightweight Ollama models (3B-4B) like Qwen or Phi-3.
- Premium VPS
- From $31.99/mo, with annual billing
- Specs: 4 CPU / 8 GB RAM / 50 GB NVMe
For complete local AI. Runs Dify combined with larger local models up to 13B, like Llama 3 or Qwen.
- Elite VPS
- From $47.99/mo, with annual billing
- Specs: 6 CPU / 16 GB RAM / 80 GB NVMe
Other requirements
- Ubuntu 24.04 LTS. Check if Docker is already installed: docker --version — if it returns a version, skip the install step below.
- SSH access to your VPS
- A domain name is optional, but required for HTTPS
- An API key from your model provider, or Ollama for local inference
Install Dify.AI on a VPS
Everything here is copy-paste ready. Placeholders are in ALL_CAPS. This is the full Dify self-hosted guide for Ubuntu, from a blank server to a working Dify.AI dashboard.
Step 1. Prepare the Server
Connect and update:
# Connect
ssh root@YOUR_SERVER_IP
# Update the system
apt update && apt upgrade -y
# Open the ports Dify needs
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

This keeps SSH open and exposes only HTTP/HTTPS. Everything else is blocked by default.
If Docker is not installed:
curl -fsSL https://get.docker.com | sh
docker compose version

The second command confirms Docker Compose is available. If it returns an error, install it separately: apt install docker-compose-plugin -y
Step 2. Install Dify
# Clone the repository
git clone https://github.com/langgenius/dify.git
# Go to the Docker directory
cd dify/docker
# Create your config file
cp .env.example .env
# If your VPS runs a control panel (Ispmanager, Hestia, etc.),
# ports 80 and 443 are already taken. Reassign Dify's ports now:
sed -i 's/EXPOSE_NGINX_PORT=80/EXPOSE_NGINX_PORT=8090/g' .env
sed -i 's/EXPOSE_NGINX_SSL_PORT=443/EXPOSE_NGINX_SSL_PORT=8443/g' .env
# Start all services
docker compose up -d

The first run pulls all Docker images (~2-4 GB). Expect 3-5 minutes on a 1 Gbps uplink.

Verify the stack is up:
docker compose ps
All containers should show the status "running". If any show "exited," run: docker compose logs SERVICE_NAME

Step 3. First Login
Go to http://YOUR_SERVER_IP:8090 (or port 80 if you skipped the port change step). The setup wizard will open.

- Create your admin account.
- Go to Settings, then Model Provider, and add your first AI model.
- Paste your API key (OpenAI, Anthropic, Gemini, or any supported provider). Dify verifies the connection immediately.
- The dashboard is ready. You can now build workflows.


Step 4 (Optional). Add Ollama for Local Models
Skip this step if you're on Medium — it doesn't have enough RAM for local models. The instructions below assume Premium (8 GB) or higher. On Premium, lightweight models (3B-4B) like Qwen or Phi-3 run comfortably. For 7B+ models, use Elite (16 GB).
Ollama lets you run models like Llama, Qwen, and Gemma on the same server as Dify.
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# Pull a model (llama3 is ~4.7 GB)
ollama pull llama3
# Confirm it loaded
ollama list


The model downloads to /usr/share/ollama/.ollama/models by default. Check available disk space first with: df -h
Fix Ollama Network Error
Two things block the connection: Ollama only listens on localhost (so Docker can't reach it), and UFW blocks port 11434.
Fix — expose Ollama to all interfaces via systemd override:
mkdir -p /etc/systemd/system/ollama.service.d
echo '[Service]' > /etc/systemd/system/ollama.service.d/environment.conf
echo 'Environment="OLLAMA_HOST=0.0.0.0"' >> /etc/systemd/system/ollama.service.d/environment.conf
systemctl daemon-reload
systemctl restart ollama
Ollama now listens on all interfaces. The override persists across reboots.
Then open the port in UFW:
# Allow Ollama access only from Docker containers (not the public internet)
ufw allow from 172.17.0.0/16 to any port 11434
# Ollama has no built-in authentication.
# Do NOT use 'ufw allow 11434/tcp' — that exposes your models to anyone.
Connect Ollama to Dify
Find your Docker bridge gateway IP:
ip route | grep docker | awk '{print $9}'
# Typically returns: 172.17.0.1

Using the bridge IP keeps traffic inside the host. If it fails, use http://YOUR_SERVER_IP:11434 instead.
Go to Settings → Model Provider, scroll to Ollama, and click Add Model. Fill in:


- Model Name: llama3
- Base URL: http://172.17.0.1:11434 (the Docker bridge IP from the terminal above)
- Model Type: LLM (default, leave as is)
- Authorization Name: Leave blank
- Completion mode: Chat (default)
- Model context size: 8192
- Upper bound for max tokens: 4096
- Vision support: No
- Function call support: No

Click Save. A green Active badge will appear next to Ollama, confirming the connection.
If the badge doesn't appear, confirm you ran both fix steps above: the systemd override and ufw allow from 172.17.0.0/16 to any port 11434Then try saving again.
Production Setup

A self-hosted AI instance on port 8090 with default credentials is a test environment. Before it handles real AI workflow traffic, add SSL, hardened secrets, and backups.
Reverse Proxy and SSL
Confirm Dify is running on port 8090 (we set this in Step 2):
cd ~/dify/docker
# Verify the port is already set from Step 2:
grep EXPOSE_NGINX_PORT .env
# Should return: EXPOSE_NGINX_PORT=8090
# If it still shows 80, run the reassignment now:
sed -i 's/EXPOSE_NGINX_PORT=80/EXPOSE_NGINX_PORT=8090/g' .env
sed -i 's/EXPOSE_NGINX_SSL_PORT=443/EXPOSE_NGINX_SSL_PORT=8443/g' .env
docker compose down && docker compose up -d
After this, Dify runs on port 8090 internally. Your system Nginx will proxy from 80/443 to 8090.
Now install Nginx and Certbot:
apt install nginx certbot python3-certbot-nginx -y
Create the Nginx config:
nano /etc/nginx/sites-available/dify
Paste this server block:
server {
listen 80;
server_name YOUR_DOMAIN;
location / {
proxy_pass http://127.0.0.1:8090;
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_buffering off;
proxy_read_timeout 3600s;
client_max_body_size 100M;
}
}
proxy_read_timeout 3600s handles long AI inference requests. client_max_body_size 100M lets you upload large documents to the knowledge base.
# Enable the site
ln -s /etc/nginx/sites-available/dify /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
# Get a free SSL certificate
certbot --nginx -d YOUR_DOMAIN
Certbot configures Nginx for HTTPS automatically and sets up auto-renewal. After this, Dify is at https://YOUR_DOMAIN
Secrets
Change all three before connecting any real data:
cd ~/dify/docker
# Generate three separate secrets
openssl rand -base64 32 # for SECRET_KEY
openssl rand -base64 32 # for DB_PASSWORD
openssl rand -base64 32 # for REDIS_PASSWORD
# Edit the .env file and update these three lines:
nano .env
SECRET_KEY=YOUR_GENERATED_SECRET
DB_PASSWORD=YOUR_GENERATED_DB_PASSWORD
REDIS_PASSWORD=YOUR_GENERATED_REDIS_PASSWORD
Run each openssl command separately and paste the output into the corresponding line. Do not reuse the same value across fields.
# Restart to apply new secrets
docker compose down && docker compose up -d
# Block direct access to Dify's internal port
ufw deny 8090/tcp
Backups
is*hosting runs weekly infrastructure backups, which cover the server itself. Dify's application data — workflows, knowledge bases, uploaded documents, and vector embeddings — lives in the dify/docker/volumes/ directory. Back that up separately.
#!/bin/bash
# Dify backup script
# Save as /home/backup_dify.sh
# chmod +x /home/backup_dify.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/home/dify_backups"
DIFY_DIR="/root/dify/docker"
mkdir -p $BACKUP_DIR
# Back up the full volumes directory
tar czf $BACKUP_DIR/dify_volumes_$DATE.tar.gz \
-C $DIFY_DIR volumes/
echo "Backup done: dify_volumes_$DATE.tar.gz"
Backs up the full volumes/ directory: DB, file storage, and vector store. DB-only backups will not restore knowledge base files on recovery.
# Schedule daily backups at 2 AM
crontab -e
# Add this line:
0 2 * * * /bin/bash /home/backup_dify.sh >> /var/log/dify_backup.log 2&1
Build Your First AI Workflow

The most useful first AI workflow to build with Dify.AI is a RAG chatbot that answers from your documents, not its training data. This is how you build your own generative AI chatbot that knows your product.
Create a Knowledge Base
- Go to Knowledge → Create Knowledge.
- Upload your documents: PDF, DOCX, TXT, or Markdown.
- Pick an embedding model. OpenAI text-embedding-3-small works for most cases.
- Wait for indexing to finish. The dashboard shows chunk count and status.
Build the Chatflow
- Go to Studio → Create App → Chatflow.
- Add a Knowledge Retrieval node and connect it to your knowledge base.
- Add an LLM node after it; this is where the model generates the response.
- Set the system prompt:


Answer questions based ONLY on the context below.
If the answer is not in the context, say so.
Do not make things up.
Context: {{#context#}}
Question: {{#sys.query#}}
{{#context#}} is automatically replaced with retrieved chunks from your knowledge base. This keeps answers grounded in your actual documents.
Publish
- Click Publish. Dify gives you an embeddable chat widget and a REST API endpoint.
- Embed the widget on your site or call the API from your app.
- Your documents are now queryable by anyone you give access to — all running on your own VPS.

Dify Cloud vs. Self-Hosted: The Numbers
|
Criteria |
Dify Cloud (Professional) |
Self-hosted on is*hosting |
|
Monthly cost |
$59/mo ($49/mo annual) |
From $21.24/mo (annual billing) |
|
Workflow run limits |
Platform quotas apply |
No platform-imposed quotas |
|
Message credits |
Limited per plan |
No credit system |
|
Your data |
Stored on Dify's servers |
Your VPS, your jurisdiction |
|
Local models |
Not supported |
Yes, via Ollama |
|
API key routing |
Through Dify's platform |
Direct to your provider |
|
Customization |
Limited by SaaS constraints |
Full access (open source) |
|
SSL |
Included |
Free via Let's Encrypt |
The cost gap is $11-38/mo, depending on the plan. Add Ollama and the savings compound: one-time compute cost, no per-token fees, no platform quotas on your AI workflow.
The real argument for self-hosting is data residency. As an open-source LLMOps platform, Dify gives you full control: customer data, legal docs, and compliance requirements stay on your server, not Dify's.
VPS
KVM-powered VPS with NVMe storage in 40+ locations. Full root access, dedicated resources, and up to 256 IPv4 addresses per plan.
Troubleshooting
Five issues most people run into the first time they run Dify.AI on a VPS:
Dify Won't Start
Check the logs for the service that failed:
docker compose logs --tail=50 api
Two causes cover most cases: RAM below 4 GB, or a port conflict.
Port conflict error: failed to bind host port 0.0.0.0:80/tcp or 443/tcp: address already in use
A system web server or control panel (Ispmanager, Hestia) owns those ports. Don't stop it; shift Dify's ports instead:
docker compose down
sed -i 's/EXPOSE_NGINX_PORT=80/EXPOSE_NGINX_PORT=8090/g' .env
sed -i 's/EXPOSE_NGINX_SSL_PORT=443/EXPOSE_NGINX_SSL_PORT=8443/g' .env
docker compose up -d
After this, Dify runs on port 8090. Check with: docker compose ps
Ollama Network Error in Dify
Dify can't reach Ollama. Two things cause this:
- Ollama only listens on localhost by default; Docker containers can't reach it.
- UFW blocks port 11434, so the connection never gets through.
Fix both:
# Step 1 - expose Ollama to all interfaces
mkdir -p /etc/systemd/system/ollama.service.d
echo '[Service]' > /etc/systemd/system/ollama.service.d/environment.conf
echo 'Environment="OLLAMA_HOST=0.0.0.0"' >> /etc/systemd/system/ollama.service.d/environment.conf
systemctl daemon-reload
systemctl restart ollama
# Step 2 - open the port for Docker only
ufw allow from 172.17.0.0/16 to any port 11434
If the Docker bridge IP (172.17.0.1) still fails in Dify after this, use your public server IP instead: http://YOUR_SERVER_IP:11434
Models Not Connecting
Test outbound connectivity from the VPS, then check the worker logs:
curl -I https://api.openai.com
docker compose logs --tail=30 worker
Some providers block outbound traffic to certain IP ranges by default. If curl returns nothing, check your firewall and VPS provider network settings.
Slow Performance
Check live resource usage across containers:
docker stats --no-stream
Consistent high memory on the API or worker container means RAM is the bottleneck. Medium (4 GB) handles API models fine. Dify + Ollama needs 12-16 GB — Elite plan territory.
502 Bad Gateway
Nginx is up, but it can't reach Dify. Verify the backend is running, and the proxy target is correct:
# Check which port Dify is actually on
docker compose ps
# Test direct connection
curl -I http://127.0.0.1:8090
If the direct connection works but Nginx returns 502, the proxy_pass in your Nginx config points to the wrong port. Match it to the port shown in docker compose ps output.
Next Steps
Medium (4 GB RAM) runs Dify.AI with API models comfortably — the core stack stays under 3 GB. For Ollama, start with Premium (8 GB) for lightweight models (3B-4B), or Elite (16 GB) for 7B-13B models.
All plans include KVM, NVMe storage, and 40+ locations. The full self-hosted AI starter kit from this guide works on any of them — the difference is how many models you can run locally.
Start with the Premium plan for local AI workflows, or compare all VPS plans.
{{ include_custom_fonts({"Whitney":["Bold","Bold Italic","Light","Light Italic","Medium","Medium Italic","Regular","Semi Bold","Semi Bold Italic"]}) }}