How to Self-Host n8n: Docker, Postgres, HTTPS and Backups
A production-minded guide to self-hosting n8n: Docker Compose with Postgres instead of SQLite, the environment variables that matter (encryption key, webhook URL, timezone), HTTPS behind a reverse proxy, backups of both the database and the encryption key, updates, and when queue mode is worth it.
Self-hosting n8n gets you unlimited workflow executions for the cost of a server, and keeps your data on infrastructure you control. The quick-start docker run command works, but for anything you rely on you want Postgres, HTTPS, a fixed encryption key and backups.
n8n's options change between versions; cross-check variable names with the n8n hosting docs.
What you need
- A Linux server with Docker and Docker Compose. 2 vCPU and 4 GB RAM is comfortable for a small team; heavy AI or data workflows want more.
- A domain or subdomain, e.g.
n8n.yourcompany.com, with an A record pointing at the server. (DNS Records Explained)
Docker Compose with Postgres
n8n defaults to SQLite, which is fine for trying it out. Postgres handles concurrent executions and larger histories better and is easier to back up properly.
# docker-compose.yml
services:
postgres:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: n8n
volumes:
- pg_data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
interval: 10s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n:latest
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
N8N_HOST: n8n.yourcompany.com
N8N_PROTOCOL: https
WEBHOOK_URL: https://n8n.yourcompany.com/
N8N_PROXY_HOPS: 1
GENERIC_TIMEZONE: Europe/Berlin
TZ: Europe/Berlin
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
volumes:
pg_data:
n8n_data:
Put secrets in a .env file next to it (and never commit it):
POSTGRES_PASSWORD=...
N8N_ENCRYPTION_KEY=...
Generate both with openssl rand -hex 32. (Generate a Secret Key)
Notes on the important lines:
127.0.0.1:5678:5678publishes n8n only on localhost, so it's reachable only through your reverse proxy — not directly from the internet. (Docker Compose Networking)N8N_ENCRYPTION_KEYencrypts the credentials stored in n8n (API keys, OAuth tokens). Set it yourself; if n8n generates one and you lose it, every stored credential becomes unreadable.WEBHOOK_URLmakes webhook nodes show your public HTTPS URL instead oflocalhost:5678.N8N_PROXY_HOPS: 1tells n8n it is behind one reverse proxy. Set this to the actual number of trusted proxy hops, and make the last proxy forwardX-Forwarded-For,X-Forwarded-HostandX-Forwarded-Proto. (n8n reverse-proxy configuration)depends_onwithservice_healthywaits for Postgres before starting n8n. (Docker Compose Healthchecks)- Pin a specific n8n version tag in production rather than
latest, so updates happen when you choose.
Start it:
docker compose up -d
docker compose logs -f n8n
HTTPS with a reverse proxy
Caddy is the shortest route — it fetches certificates automatically:
n8n.yourcompany.com {
reverse_proxy 127.0.0.1:5678
}
With Nginx, remember n8n's editor uses WebSockets, so pass the Upgrade and Connection headers through. (Caddy vs Nginx vs Traefik, Nginx Reverse Proxy Configuration)
Then open https://n8n.yourcompany.com and create the owner account straight away — until you do, anyone who finds the URL can.
Backups: two things, not one
- The database — workflows, credentials (encrypted) and execution history:
docker compose exec -T postgres pg_dump -U n8n -Fc n8n > n8n-$(date +%F).dump
- The encryption key — store it in your password manager. A database backup without the key restores workflows but not their credentials.
Copy backups off the server and test a restore. (How to Back Up a Postgres Database, The 3-2-1 Backup Rule)
Keep it healthy
- Prune execution data so the database doesn't grow forever: n8n has
EXECUTIONS_DATA_PRUNEandEXECUTIONS_DATA_MAX_AGEsettings. - Update deliberately: read the release notes, back up, change the image tag,
docker compose pull && docker compose up -d. - Lock down the server: firewall, SSH keys, automatic security updates. (Secure a New VPS)
When to use queue mode
By default one n8n process runs everything. Queue mode adds Redis and separate worker containers so executions are spread across workers — worth it when you have many concurrent or long-running workflows. Most small teams don't need it.
The licence
Self-hosting n8n for your own business is free under its Sustainable Use License. Offering n8n itself as a service to others isn't allowed without a commercial agreement, and some enterprise features require a paid licence.
The summary
- Use Docker Compose with Postgres, a fixed
N8N_ENCRYPTION_KEYandWEBHOOK_URL. - Bind n8n to localhost and put HTTPS in front with Caddy or Nginx (WebSockets on).
- Back up the database and the encryption key; prune old executions.
EasySpawn gives you a managed server with HTTPS, Postgres and daily backups handled, plus Claude Code to build the app and workflows that run on it. See how it works or join the waitlist.
Related: n8n vs Zapier · Docker Compose for Local Development · Docker Volumes vs Bind Mounts · What Is Caddy?
Keep reading
How to Reduce Docker Image Size: From 1.5 GB to Under 200 MB
Big Docker images are slow to build, push, pull and deploy. The techniques that actually shrink them: slim base images, multi-stage builds, .dockerignore, production-only dependencies, layer ordering, cache cleanup — with Node.js and Python examples and a way to see what's taking space.
Writing a Production Dockerfile for a Node.js App
The Dockerfile an AI tool writes usually works — and ships a 1.5 GB image running as root that ignores shutdown signals and leaks build secrets into its layers. A line-by-line production Dockerfile: multi-stage builds, layer caching, non-root users, signal handling, secrets, and health checks.