Contents
Self-Hosting n8n Tutorial: Docker Compose, PostgreSQL, and HTTPS
After testing successfully on your own computer, you need to deploy n8n to a server with a fixed URL so your workflows can run 24/7 and external services such as Webhook and Telegram can connect to them.
What you need
- A Linux cloud server (VPS). For light use, 1–2 CPU cores and 2 GB of memory are enough; you'll need more for many workflows or to run local AI models.
- A domain, with the DNS A record for a subdomain (such as
n8n.example.com) pointing to the server's IP address. - Docker and Docker Compose installed on the server (see Docker tutorial: installation).
- Ports 80 and 443 open in the firewall.
Architecture
Internet ──HTTPS──▶ Caddy (automatic certificates) ──▶ n8n:5678 ──▶ PostgreSQL
- Caddy: Reverse proxy that automatically obtains and renews Let's Encrypt certificates
- PostgreSQL: The recommended database for production (the default SQLite database is suitable for personal use or testing)
- Neither n8n nor the database is exposed directly to the internet
Starting with n8n 2.x, MySQL / MariaDB are no longer supported as databases for n8n. Use PostgreSQL or SQLite.
Files
Create the /opt/n8n directory on the server:
/opt/n8n/
├── compose.yaml
├── .env
└── Caddyfile
.env
DOMAIN=n8n.example.com
POSTGRES_PASSWORD=Replace with a long, random password
N8N_ENCRYPTION_KEY=Replace with a different long, random string
Generate a random string:
openssl rand -hex 32
N8N_ENCRYPTION_KEYencrypts all credentials. Be sure to back it up separately and securely; if you lose it, none of your credentials will be usable.
Caddyfile
{$DOMAIN} {
reverse_proxy n8n:5678
}
compose.yaml
services:
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
environment:
DOMAIN: ${DOMAIN}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
restart: unless-stopped
postgres:
image: postgres:17
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: n8n
volumes:
- pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
interval: 5s
retries: 10
restart: unless-stopped
n8n:
image: n8nio/n8n:2.42.7 # Change this to the fixed version you want to use
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
N8N_HOST: ${DOMAIN}
N8N_PORT: 5678
N8N_PROTOCOL: https
N8N_WEBHOOK_URL: https://${DOMAIN}/
N8N_PROXY_HOPS: 1
NODE_ENV: production
GENERIC_TIMEZONE: Asia/Taipei
TZ: Asia/Taipei
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true"
EXECUTIONS_DATA_PRUNE: "true"
EXECUTIONS_DATA_MAX_AGE: 336
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
volumes:
caddy_data:
pg_data:
n8n_data:
Important environment variables
| Variable | Description |
|---|---|
N8N_HOST, N8N_PROTOCOL | The public domain and protocol; these affect OAuth callback URLs and other settings. |
N8N_WEBHOOK_URL | The public URL for webhooks. If you don't set it, Webhook nodes will show http://localhost:5678/.... For versions earlier than n8n 2.30, use WEBHOOK_URL instead. |
N8N_PROXY_HOPS | Set this to 1 when n8n is behind a reverse proxy so it can get the user's real IP address. |
N8N_ENCRYPTION_KEY | The key used to encrypt credentials |
GENERIC_TIMEZONE | The time zone used for scheduled workflows |
EXECUTIONS_DATA_PRUNE, EXECUTIONS_DATA_MAX_AGE | Automatically delete execution records older than the specified number of hours (336 hours = 14 days) to prevent the database from growing indefinitely. |
Start n8n
cd /opt/n8n
docker compose up -d
docker compose logs -f n8n
Within a few minutes, Caddy will obtain a certificate automatically. Open https://n8n.example.com and create an owner account.
Create the account right away. Until you set up the owner account, anyone who visits this URL can create an account first.
Security recommendations
- Use a fixed image tag; don't use
latest. Read the release notes before upgrading. - Open only ports 22, 80, and 443 on the server, and allow SSH key-based login only.
- Enable two-step verification for your account (in personal settings).
- Dangerous nodes you don't need (such as nodes that run system commands) are disabled by default in 2.x. Don't enable them casually.
- Back up regularly (see Backup and updates).
Connect a local AI model
To let n8n use a local Ollama model, add an ollama service to the same compose.yaml file. See Connect n8n to Ollama. AI models need more memory and computing resources, so a typical small VPS can't run them.
High-volume executions: queue mode
When you have many workflows that run frequently, you can enable queue mode: the main process receives triggers and hands them to multiple workers for parallel execution through a Redis queue. This is an advanced deployment setup; see the queue mode section in the official documentation.
Updated October 12, 2026