//------------------------------------------------------------------- //-------------------------------------------------------------------
self-host Dify with Docker Compose

How to Self-Host Dify with Docker Compose, HTTPS, Persistent Storage, and External PostgreSQL Export

Dify is an open-source platform for building AI apps like chatbots, agents, and RAG systems using a visual, drag-and-drop workflow builder instead of writing lots of code. It supports more than 100 LLM providers and lets you deploy to the cloud or self-host it yourself with Docker Compose.

Teams choose to self-host Dify with Docker Compose because it gives full control over data, model provider keys, and infrastructure spend, without depending on Dify’s managed cloud.

This guide covers the full setup from a blank server to a fully working instance with HTTPS, persistent volumes, and external PostgreSQL.

Requirements Before You Begin

Before you self-host Dify with Docker Compose, make sure your server and tools are ready:

  • Linux server running Ubuntu LTS with at least 2 CPU cores and 4GB RAM.
  • Docker Engine 19.03+ and Docker Compose 2.24.0+.
  • A domain name with an A record pointing to your server’s public IP.
  • Ports 80 and 443 open in your firewall or cloud security group.
  • API key for at least one model provider, like OpenAI, Anthropic, Azure, or a self-hosted runner like Ollama.

This stack runs 14+ containers at once, so a small VPS can struggle once you get more users. A strong dedicated server gives you dedicated CPU and RAM, so the database, vector store, and workers all have room to run smoothly.

Verify the required tools are installed correctly on your server:

git --version
curl --version
jq --version
docker compose version

Now proceed to the following steps to self-host Dify with Docker Compose.

Step 1. Clone the Dify Repository

This is the first step to self-host Dify with Docker Compose. You must pull the latest tagged release, not the unstable main branch. To do this, run the commands below:

git clone --branch "$(curl -s https://api.github.com/repos/langgenius/dify/releases/latest | jq -r .tag_name)" https://github.com/langgenius/dify.git
cd dify/docker

Step 2. Create and Edit the .env File

At this point, you must copy the example file so you have a working environment file to edit:

cp .env.example .env

This .env file controls every setting when you self-host Dify with Docker Compose.

Now you must generate a secret key, because Dify uses it to sign session cookies and encrypt stored credentials.

Use this command to generate the secret key:

openssl rand -base64 42

Copy the output and keep it in a safe place.

Open the .env file with your desired text editor:

nano .env

In the file, look for the line below and paste your secret key:

SECRET_KEY=paste_the_generated_value_here

Also, find these lines and fill them with your domain name:

CONSOLE_API_URL=https://yourdomain.com
CONSOLE_WEB_URL=https://yourdomain.com
SERVICE_API_URL=https://yourdomain.com
APP_API_URL=https://yourdomain.com
APP_WEB_URL=https://yourdomain.com
NEXT_PUBLIC_SOCKET_URL=wss://yourdomain.com

Once you are done, save and close the file. If you still need a domain name, you can check PerLod’s domain service registration to register and point a domain at your server in minutes.

Step 3. Configure HTTPS With Let’s Encrypt and Access Dify Dashboard

A production setup where you self-host Dify with Docker Compose should never run on plain HTTP long-term. The bundled Nginx supports Certbot for automated Let’s Encrypt certificates.

Edit the .env file again and set:

nano .env
NGINX_SERVER_NAME=yourdomain.com
NGINX_HTTPS_ENABLED=false
NGINX_SSL_CERT_FILENAME=fullchain.pem
NGINX_SSL_CERT_KEY_FILENAME=privkey.pem
NGINX_ENABLE_CERTBOT_CHALLENGE=true

Also, you must add these two lines anywhere in the file:

CERTBOT_DOMAIN=yourdomain.com
CERTBOT_EMAIL=yo*@********in.com

Save and close the file. Keep NGINX_HTTPS_ENABLED=false for now, because no certificate exists yet.

Start the stack with the certbot profile so Nginx can respond to the ACME challenge:

docker compose --profile certbot up --force-recreate -d
Start the Dify stack with certbot

Now issue the certificate itself. Run the certbot container’s helper script:

docker compose exec -it certbot /bin/sh /update-cert.sh

You must get:

Successfully received certificate.
Certificate is saved at: 
...

Once the certificate is issued, go back to .env and turn on HTTPS:

# in .env
NGINX_HTTPS_ENABLED=true

Then, use the command below to reload only Nginx:

docker compose --profile certbot up -d --no-deps --force-recreate nginx

Note: If nginx or plugin_daemon keeps restarting after you self-host Dify with Docker Compose, run docker compose ps -a and check that db_postgres actually started. It can silently fail to launch on the first up. Starting it manually with docker compose up -d db_postgres before restarting the full stack usually fixes both errors at once.

Now you open your browser and visit:

https://yourdomain.com

You must see the setting up an admin account screen. Create your Dify admin account and click Set up.

Dify admin account setup

You have now accessed Dify’s Home / Templates page.

Dify's Home / Templates

Note: Let’s Encrypt certificates expire every 90 days, so keep certbot’s renewal cron active, or repeat the update-cert.sh step and reload Nginx manually every few months.

With login working and the console loaded, you’ve confirmed the core Dify install is successful. Follow the steps below to configure Dify to use an external PostgreSQL database.

Step 4. Point Dify at an External PostgreSQL Database

The default Compose file runs its own db_postgres container, which works fine for quick tests. But to properly self-host Dify with Docker Compose in production, using a separate and managed PostgreSQL instance is safer for backups, patching, and uptime.

First, on your external PostgreSQL server, create the two databases Dify needs. One for the app and one for the plugin daemon:

CREATE DATABASE dify;
CREATE DATABASE dify_plugin;
CREATE USER dify_user WITH ENCRYPTED PASSWORD 'your_strong_password';
GRANT ALL PRIVILEGES ON DATABASE dify TO dify_user;
GRANT ALL PRIVILEGES ON DATABASE dify_plugin TO dify_user;

Then edit .env on your Dify server:

nano .env

Update the database block:

DB_USERNAME=dify_user
DB_PASSWORD=your_strong_password
DB_HOST=your-external-postgres-host.example.com
DB_PORT=5432
DB_DATABASE=dify
DB_PLUGIN_DATABASE=dify_plugin
SQLALCHEMY_POOL_PRE_PING=true

Setting SQLALCHEMY_POOL_PRE_PING=true matters here, since external connections over a network drop more than a local Docker network link.

Next, remove the bundled db_postgres service from COMPOSE_PROFILES so it doesn’t start pointlessly:

# in .env, change this line:
COMPOSE_PROFILES=${VECTOR_STORE:-weaviate},collaboration

This tells Docker Compose to skip the local db_postgres container completely, since it’s no longer needed. If many users will use Dify at once, also raise POSTGRES_MAX_CONNECTIONS on your external database, so it has enough space for all your workers and Celery tasks.

Step 5. Set Up Persistent Storage

Every stateful service needs a volume that survives container restarts, or you lose data every time you redeploy. By default, STORAGE_TYPE=opendal with OPENDAL_SCHEME=fs and OPENDAL_FS_ROOT=storage stores uploaded files on a local Docker volume.

For a setup where you truly self-host Dify with Docker Compose in production, moving file storage off local disk to S3-compatible object storage removes a single point of failure.

Edit .env:

nano .env

Set:

STORAGE_TYPE=s3
S3_ENDPOINT=https://s3.yourregion.amazonaws.com
S3_BUCKET_NAME=your-dify-bucket
S3_ACCESS_KEY=your-access-key
S3_SECRET_KEY=your-secret-key
S3_REGION=your-region

Weaviate (the default vector store) also saves its data to its own volume using WEAVIATE_PERSISTENCE_DATA_PATH=/var/lib/weaviate, and Redis keeps its cache and Celery data in its own volume too. It’s best to leave both as Docker-managed volumes unless you have a good reason to move them.

Step 6. Add Model Provider Secrets

To actually use Dify once it’s running, add at least one model provider key. The cleanest way is through the console after login, not .env, but if you want a provider pre-filled at first boot, you can add it directly in .env:

OPENAI_API_KEY=sk-yourkeyhere

Most people skip this and instead add a provider after their first login. Open the Marketplace in the left sidebar, search for your provider (OpenAI, Anthropic, Azure OpenAI, Gemini, or self-hosted runners like Ollama or vLLM), and click Install.

Dify Model Providers
Install an integration on Dify

Once installed, go to Integrations > Model Provider in the left sidebar, where you can paste in your API key or add your model.

Add your API key or model in Dify integrations

Step 7. Launch the Full Dify Stack

With your model provider connected and a default model set, Dify is fully configured. Now launch the complete stack and verify every service is running correctly:

docker compose up -d

Watch the logs to confirm every service starts cleanly and database migrations finish:

docker compose logs -f

The api container runs migrations automatically on first boot since MIGRATION_ENABLED=true by default. Give it a minute or two before checking status.

Check that all containers came up correctly:

docker compose ps

Keep Your Dify Deployment Updated

To update a server where you self-host Dify with Docker Compose, pull the newest release and rebuild:

git pull
docker compose pull
docker compose down
docker compose up -d

After updating, always compare your .env.example with your live .env file. New releases often add new variables or change default values, so this helps you catch anything you need to update.

Conclusion

This setup is a strong Dify deployment including proper HTTPS, a database you fully control, and file storage that won’t disappear on restart. Once your usage grows, go for a dedicated server, which gives the database, vector store, and background workers enough space that they need to run smoothly.

We hope you enjoy this guide. For more detailed information, check the Official Dify Docker Compose Docs.

FAQs

Do I need a domain to self-host Dify with Docker Compose?

No, you can use your server’s IP address, but a domain is required to get a valid Let’s Encrypt HTTPS certificate.

Can I run Dify without PostgreSQL?

No, PostgreSQL is required by default to store app, user, and plugin data.

How much RAM does Dify actually need?

At least 4GB for testing; production setups with several concurrent users usually need 16GB or more.

Post Your Comment

PerLod delivers high-performance hosting with real-time support and unmatched reliability.

Contact us

Payment methods

payment gateway
Perlod Logo
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.