Why Host Dify on a VPS?
Using the cloud version of Dify means entrusting your prompts, your business data and your API keys to a third-party server, with limits on the number of applications and models you can access. By self-hosting Dify on a VPS, you remove these constraints: no monthly usage-based subscription, no sensitive data sent outside, and the freedom to connect any model provider (OpenAI, Anthropic, Ollama locally, etc.). It is the ideal solution for teams that want to build robust AI pipelines in an environment under their full control.
What You Can Do With Dify
- Create custom chatbots and AI agents connected to your own data sources (RAG)
- Orchestrate multi-step workflows combining several language models and external tools
- Connect Dify to OpenAI, Anthropic Claude, Mistral, or a local model via Ollama
- Expose your AI applications via a REST API or an embeddable widget for your site
- Manage your team's access with a system of roles and workspaces
- Track usage logs, per-model costs and the performance of your pipelines
Prerequisites: Sizing Your VPS Correctly
Dify runs a fleet of Docker services in parallel: an API server, a Celery worker for embeddings and async jobs, the web frontend, PostgreSQL (the db_postgres service), Redis, Weaviate as the default vector store (VECTOR_STORE=weaviate), a code-execution sandbox, the plugin_daemon, SSRF proxies (Squid) and nginx. Since 1.16.0, agent_backend and local_sandbox join them to run Agent applications.
Official minimum: 2 CPU cores and 4 GiB of RAM, with Docker Compose 2.24.0 or newer (Dify documentation). Plan 2 vCPU · 4 GB · 40 GB SSD to test the basic features.
Recommended for real use: 4 vCPU · 8 GB RAM · 80 GB storage. As soon as you index large documents, several users query at once or you enable Agent applications, embeddings, the vector store and the agent services saturate 4 GB. The ServOrbit VPS Power plan (4 vCPU, 8 GB) suits a team workload without a local model.
With a local model (Ollama): add the model's own footprint. A quantised Llama 3 8B (Q4) needs ~6 GB of VRAM or dedicated RAM; plan for at least 16 GB RAM to run Dify and Ollama side by side without swap.
Recommended OS: Ubuntu 22.04 LTS.
Install Dify on Your ServOrbit VPS
Order a ServOrbit Cloud VPS
Go to the /vps-cloud page and choose a plan suited to your usage (minimum 2 vCPU / 4 GB RAM, recommended 4 vCPU / 8 GB). Select Ubuntu 22.04 LTS as the operating system, then complete the order. Your VPS is provisioned in under a minute.
Clone a published release and copy the configuration
Connect via SSH, then run
git clone --branch 1.18.0 https://github.com/langgenius/dify.git,cd dify/dockerandcp .env.example .env. Pin a release tag rather than themainbranch, which moves with every merge: the official documentation asks you to clone the latest release.The
.envfile holds the configuration. Before the first start, replace the development values it ships with:DB_PASSWORD(defaultdifyai123456),REDIS_PASSWORD,SANDBOX_API_KEY,PLUGIN_DAEMON_KEY,DIFY_AGENT_API_TOKENandDIFY_AGENT_SERVER_SECRET_KEY. LeaveSECRET_KEYempty so a persistent key is generated: changing it later logs every user out and makes plugin OAuth credentials unreadable. Finally, generate the agent sandbox token withopenssl rand -base64 32and put it inDIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN: without it, the Agent backend and its sandbox do not start.Start the Docker Compose stack
From
dify/docker, rundocker compose up -d. Docker pulls the images and starts the containers:api,worker,worker_beat,web,db_postgres,redis,weaviate,sandbox,plugin_daemon,ssrf_proxy,agent_backend,local_sandboxandnginx. Check withdocker compose psthat services areUporhealthy;init_permissionsshowsExited, which is expected. The operation typically takes 3 to 5 minutes depending on your bandwidth.Create the administrator account
Open
http://[YOUR-VPS-IP]/installin your browser.Warning: do this immediately. As long as the admin account does not exist, the
/installpage is open to anyone, and whoever arrives first becomes the owner of your instance. If you setINIT_PASSWORDin.env(30 characters maximum), the page asks for it before creating the account.Enter your e-mail address and a strong password. This account is the owner of the default workspace.
Point a domain name and enable HTTPS
For production, associate a domain name with the VPS IP and deploy a TLS certificate. Dify's internal nginx listens on host port 80 by default (
EXPOSE_NGINX_PORT=80): if you install an nginx on the host, the two collide. SetEXPOSE_NGINX_PORT=8080in.env, then install nginx and Certbot on the host (sudo apt install nginx certbot python3-certbot-nginx -y), create a vhost that proxies to127.0.0.1:8080and runsudo certbot --nginx -d your-domain.com.Then set
CONSOLE_API_URL,APP_WEB_URL,SERVICE_API_URLandFILES_URLtohttps://your-domain.com(empty by default):CONSOLE_API_URLserves OAuth logins and plugins,APP_WEB_URLthe Human Input node links, and ifFILES_URLandCONSOLE_API_URLboth stay empty, file previews do not work. Restart withdocker compose up -d.
Post-Install Configuration: Models, API and Workspace
Once logged in, the first step is to register a model provider. In settings (icon top-right) → Model Providers, add your OpenAI, Anthropic, Mistral or any other supported API key. For a local model, enter your Ollama instance URL (http://[host-IP]:11434).
Dify also exposes its own REST API so you can integrate your applications into external pipelines. Find your API keys under Settings → API Keys. These keys allow you to query your Dify apps from n8n, Zapier or any Python script without going through the GUI.
The default workspace is the one created at first login. Invite members via Settings → Members and assign them Admin, Normal or Dataset operator roles.
Offline Plugins: Working Around the 500/404 Error on Self-Hosted Instances
Since November 2025 (GitHub issue #27720, opened on Dify 1.9.2), self-hosted users see plugin installation from the marketplace fail: HTTP 500 for langgenius/perplexity, HTTP 404 for langgenius/anthropic, then Reached maximum retries (3) for langgenius/azure_openai. The same plugins install on Dify Cloud. The issue was closed as not planned on 18 December 2025 with the cant-reproduce label and no fix: the workarounds below come from a bot in the thread and were not confirmed by maintainers.
Workaround 1: local .difypkg file. Download the plugin's .difypkg file on a connected machine, then install it with "Install from local file". For a server without outbound access, the dify-plugin-repackaging tool bundles dependencies into the package. If the file is rejected for its size, raise PLUGIN_MAX_PACKAGE_SIZE (52428800 bytes by default) and NGINX_CLIENT_MAX_BODY_SIZE (100M by default).
Workaround 2: disable signature verification. To install unsigned plugins or develop your own, set FORCE_VERIFYING_SIGNATURE=false (default: true) in .env, then run docker compose restart plugin_daemon.
Warning: disabling signatures removes a security guard. Keep this setting for development or plugins whose source you control; in production with third-party users, prefer a verified .difypkg.
Known Limits of the Docker Install: the Agent Node
Agent node in Chatflow and Workflow on 1.15.0. On the standard Docker install of 1.15.0, the "Agent" node fails before any model call with ValueError: base_url is required when creating a real Agent backend client. It needs an "Agent backend" service pointed to by AGENT_BACKEND_BASE_URL, which the official docker-compose.yaml of that version did not contain. The langgenius/dify issues #38283 (closed as not planned on 17 August 2026) and #39161 (closed as a duplicate on 23 October 2026) produced no fix; a better error message is still under review (PR #39878). Two workarounds circulate and neither fits: AGENT_BACKEND_USE_FAKE=true plugs in a fake client that returns a hard-coded answer and never calls the LLM or the MCP tools, and deploying a dify-agent service by hand is neither documented nor supported.
Since 1.16. The official compose now ships agent_backend and local_sandbox, with AGENT_BACKEND_BASE_URL=http://agent_backend:5050 in .env.example, and you must set DIFY_AGENT_API_TOKEN (which has to equal AGENT_BACKEND_API_TOKEN since 1.16.1) and DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN. But Agent V2 was hidden from Chatflow as early as 1.16.1, for lack of multi-turn context; the request to restore it (#41269, opened on 26 August 2026) has no target version. In Chatflow, only the legacy Agent node is available. The standalone Agent application type is the alternative the reporter of #39161 says worked on 1.15.0.
Known Limits: Editing a Chatflow With an MCP Tool on 1.18.0
Issue #44742 (opened on 25 October 2026) reports that after removing and re-adding an MCP tool in an Agent node on 1.18.0, the Chatflow no longer runs: TypeError: object of type 'NoneType' has no len(), raised in build_parameters. The exported YAML shows allowed_tools, context and files saved as type: variable with value: null. Another MCP tool left untouched in the same flow keeps working. Going back to 1.17.x does not repair a flow that is already damaged, and importing the broken export into another instance fails too.
Reported workarounds. Edit the YAML to set these three fields to type: constant with value: [], then import it as is (repackaging it as a .difypkg fails the integrity check), or rebuild the flow.
State of the thread. The issue was closed on 26 October 2026 by PR #44748, merged into main: it no longer writes cleared optional fields into the saved graph. It resolves #44579 (AgentVariableNotFoundError), but states that nested manual parameters and the full remove/re-add MCP sequence are outside its scope. No release after 1.18.0 appeared on the releases page on 28 October 2026. Until then, export the YAML before every edit of a Chatflow that uses an MCP tool.
Connecting Ollama and n8n
Dify becomes particularly powerful combined with other self-hosted tools.
Ollama lets you run an LLM directly on your VPS or a server on the same network, without depending on an external API. After deploying Ollama, register it in Dify as a model provider with the URL http://[Ollama-IP]:11434. All generation then stays on your infrastructure — zero prompt leakage to the outside.
n8n is a workflow orchestrator that can drive Dify via its REST API. Create an HTTP Request node in n8n pointing to https://your-domain.com/v1/chat-messages with your Dify API key in the Authorization header. You can then trigger a Dify conversation from a web form, an inbound e-mail or a Slack webhook, and send the AI response to any destination.
Back Up the Volume and the Database Before Upgrading
A Dify upgrade touches the PostgreSQL schema (migrations run when the API starts as long as MIGRATION_ENABLED=true), the vector data and the agent stack images. The 1.18.0 notes open with "Back up first": database, vector store data, uploaded files, Agent storage, secrets and environment files.
On Docker Compose, almost everything lives in docker/volumes/ (db/data for PostgreSQL, weaviate, redis/data), as folder mounts rather than named volumes. For a consistent copy: docker compose stop, then tar czf ../dify-backup.tar.gz volumes .env docker-compose.yaml, then copy the archive off the VPS. Without a full stop, a logical export is still possible: docker compose exec -T db_postgres pg_dump -U postgres dify > dify-dump.sql.
Also keep, outside the repository, SECRET_KEY and PASSWORD_PEPPER if you set one: losing the first makes encrypted credentials unreadable, and changing the second invalidates existing password hashes (1.18.0 notes). Finally, plan the steps: if your stack still bundles Weaviate 1.27, the 1.17.1 notes require a staged move to 1.39, with no minor-version skipping.
Upgrade Safely: Rehearse on a Copy First
Freeze the current state
Note the running version (
docker compose images), stop the stack withdocker compose stopand take the backup described above. Check that the archive opens (tar tzf) before going further: a backup that was never read back is not a backup.Duplicate the instance
From the parent folder, run
cp -a dify dify-test. Indify-test/docker/.env, changeEXPOSE_NGINX_PORTandEXPOSE_NGINX_SSL_PORT(for example 8081 and 8444) so they do not collide with production, and leave disk and RAM for a second stack. The Compose project name follows the folder name, which keeps the two stacks apart.Move the copy to the target version
In
dify-test, rungit fetch --tagsthengit checkout <tag>. Compare each.env.examplewith your.envto spot new or renamed variables, and if you editeddocker-compose.yaml, re-apply your changes onto the new release's file instead of keeping the old one. Rundocker compose pullthendocker compose up -dand followdocker compose logs -f apiuntil the migrations finish. From 1.17.1 to 1.18.0 the notes count 22 of them: API, workers, Agent Backend and runtime images move together.Replay your sensitive flows
On the copy, open every Chatflow that uses an Agent node or an MCP tool, run it, then edit it and save it once: editing is what broke flows on 1.18.0. Also test a knowledge base query, a plugin call and a YAML export. Then destroy the copy:
docker compose down, then delete thedify-testfolder.Switch production, or roll back
Replay the same steps in production during a maintenance window. To roll back, do not just switch the tag back: the migrations changed the database. Run
docker compose down, restore the volumes archive, then return to the previous tag.
Troubleshooting: The Most Common Errors
worker container restarting in a loop. The Celery worker depends on Redis and PostgreSQL. Check their status with docker compose ps db_postgres redis and read docker compose logs worker --tail=50. If Redis was not ready at startup, a docker compose restart worker is enough.
plugin_daemon looping on the PostgreSQL connection. Report dify-plugin-daemon #729 (Dify 1.14.0) shows failed to connect to host=db_postgres … lookup db_postgres on 127.0.0.11:53: no such host, then panic: failed to init dify plugin db. The host name must match a running service: check that db_postgres is healthy, that DB_HOST in .env carries that name and that COMPOSE_PROFILES contains postgresql, then run docker compose down and docker compose up -d. That issue was closed without a confirmed cause.
A service fails to start because of vm.max_map_count. This kernel setting concerns Elasticsearch (the elasticsearch profile), not Weaviate. If you chose it, Elastic documentation asks for 1048576 when the default is lower: sudo sysctl -w vm.max_map_count=1048576, then the same line in /etc/sysctl.conf and sudo sysctl --system to make it permanent.
Marketplace plugins: 500/404 error. See the "Offline Plugins" section. If a network block is involved, test outbound 443 from a container: docker compose exec api curl -I https://marketplace.dify.ai.
WebSocket disconnected behind a reverse proxy. If response streaming stops, your external nginx does not forward the upgrade headers. Add in location /: proxy_set_header Upgrade $http_upgrade; and proxy_set_header Connection "upgrade";.
Troubleshooting: Agents, MCP Tools and Upgrades
base_url is required when creating a real Agent backend client. See "Known Limits of the Docker Install: the Agent Node". On a recent version, check that agent_backend is running and that AGENT_BACKEND_BASE_URL points to http://agent_backend:5050.
agent_backend or local_sandbox will not start. The DIFY_AGENT_LOCAL_SANDBOX_AUTH_TOKEN value is empty: the documentation states that the Agent backend and its sandbox do not start without it. Generate it with openssl rand -base64 32, put it in docker/.env and restart.
TypeError: object of type 'NoneType' has no len(). Chatflow damaged after editing an MCP tool on 1.18.0 (#44742): see the dedicated section, and fix the YAML before importing.
AgentVariableNotFoundError: Variable '' does not exist. A cleared optional field in the Agent node leaves an empty selector in the saved graph (#44579, seen on 1.17.1). Put a value back in the field, or wait for a release that includes PR #44748.
Real IP lost behind Cloudflare or a load balancer. Since 1.18.0, the bundled nginx replaces X-Forwarded-For: configure trusted real-IP handling.
Cannot log in after an update. A PASSWORD_PEPPER that was added, changed or removed invalidates existing passwords: restore the original value.
Take an on-demand ServOrbit snapshot from the client area before every upgrade: it covers the docker/volumes/ folder (PostgreSQL, Weaviate, Redis) and your .env files. Stop the stack first with docker compose stop so the database is copied in a consistent state. If an update goes wrong, rollback takes just a few minutes from the client area. The snapshot complements the test copy, it does not replace it.
The Official Documentation
For advanced configuration, the full list of environment variables and release notes, refer to the official Dify documentation. The docker-compose.yaml in the official repository remains the source of truth for image versions and inter-service dependencies.