Updated October 2026 · Tested with n8n 2.x
Self-hosting n8n gives you unlimited executions, full control over your data and the freedom to install any community node, all for the price of a server. It also makes you responsible for security, updates and backups.
This guide walks you through a production-ready self-hosted n8n setup in 2026: what you need, a complete Docker Compose stack (PostgreSQL, Redis queue mode, workers and the new external task runners), HTTPS, backups, updates and the mistakes that break most installations.
In a hurry? If you just want to try n8n on your laptop, follow our step-by-step Docker installation. If you want self-hosted n8n without running the server yourself, managed n8n hosting gives you the same Community Edition with updates and backups handled for you.
What is self-hosted n8n?
n8n is distributed as the free Community Edition that you can run on your own infrastructure. It is “fair-code” under n8n’s Sustainable Use License: free for internal business use, with paid Business and Enterprise license keys that unlock extra features such as SSO and advanced permissions. If you plan to offer n8n itself to third parties, read the license terms first.
Why self-host n8n?
- Unlimited executions: no monthly execution quota like on n8n Cloud.
- Data control: workflows, credentials and execution data stay on your servers, which helps with GDPR and data-residency requirements.
- Any community node: install community nodes from npm, not only verified ones.
- Custom environment: add your own npm packages and Python libraries to Code nodes, connect to private networks and databases.
- Predictable cost: you pay for a server, not per execution.
When self-hosting is the wrong choice
If nobody on your team is comfortable with Linux, Docker, DNS and SSL, a self-hosted n8n will eventually break at the worst moment. In that case, choose n8n Cloud or managed n8n hosting instead.
Requirements
- A Linux server (Ubuntu 24.04 LTS is a safe choice) from any VPS provider.
- Docker and Docker Compose. Docker Compose is n8n’s recommended method for production. The npm installation method is being phased out.
- A domain or subdomain (for example
n8n.example.com) pointing to your server, so you can get an SSL certificate and receive webhooks. - Ports 80 and 443 open. Keep port 5678 closed to the internet.
| Workload | vCPU | RAM | Disk |
|---|---|---|---|
| Testing / personal | 1 | 2 GB | 20 GB |
| Small team | 2 | 4 GB | 40 GB |
| Production with queue mode | 4+ | 8 GB+ | 80 GB+ |
Architecture of a production n8n setup
- n8n main: serves the editor, the API and webhooks.
- PostgreSQL: stores workflows, credentials and executions. Use it instead of SQLite for production.
- Redis + queue mode: the main instance puts executions in a Redis queue, and workers process them. This keeps the editor fast and lets you scale horizontally.
- Workers: one or more
n8n workercontainers that run the executions. - Task runners: since n8n 2.x, JavaScript and Python Code nodes run in a separate
n8nio/runnerscontainer (“external mode”). In queue mode, each worker gets its own runner sidecar. - Reverse proxy: Caddy, Traefik or Nginx terminates HTTPS and forwards traffic to n8n.
Step 1: Prepare the server
# Install Docker (official convenience script)
curl -fsSL https://get.docker.com | sh
# Create a project folder
mkdir -p ~/n8n && cd ~/n8n
Point an A record for your n8n subdomain to the server’s IP address before you continue.
Step 2: Create the .env file
Generate strong random secrets and keep this file private. Back up N8N_ENCRYPTION_KEY somewhere safe: every credential in n8n is encrypted with it.
cat > .env <<EOF
N8N_VERSION=2.43.7
N8N_DOMAIN=n8n.example.com
POSTGRES_PASSWORD=$(openssl rand -hex 24)
N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
RUNNERS_AUTH_TOKEN=$(openssl rand -hex 32)
EOF
chmod 600 .env
Replace N8N_VERSION with the current stable release. Always pin an exact version instead of latest, so updates happen only when you decide.
Step 3: Create docker-compose.yml
This stack runs n8n in queue mode with PostgreSQL, Redis, one worker, external task runners and Caddy for automatic HTTPS.
x-n8n-env: &n8n-env
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_DOMAIN}
N8N_PROTOCOL: https
N8N_WEBHOOK_URL: https://${N8N_DOMAIN}/ # n8n 2.30 and newer
WEBHOOK_URL: https://${N8N_DOMAIN}/ # older versions
N8N_PROXY_HOPS: "1"
GENERIC_TIMEZONE: Europe/Berlin
TZ: Europe/Berlin
EXECUTIONS_MODE: queue
QUEUE_BULL_REDIS_HOST: redis
QUEUE_HEALTH_CHECK_ACTIVE: "true"
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: "true"
N8N_RUNNERS_MODE: external
N8N_RUNNERS_AUTH_TOKEN: ${RUNNERS_AUTH_TOKEN}
N8N_RUNNERS_BROKER_LISTEN_ADDRESS: 0.0.0.0
N8N_BLOCK_ENV_ACCESS_IN_NODE: "true"
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: n8n
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
interval: 10s
retries: 10
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
retries: 10
n8n:
image: n8nio/n8n:${N8N_VERSION}
restart: unless-stopped
environment: *n8n-env
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
n8n-worker:
image: n8nio/n8n:${N8N_VERSION}
command: worker
restart: unless-stopped
environment: *n8n-env
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- n8n
task-runners:
image: n8nio/runners:${N8N_VERSION}
restart: unless-stopped
environment:
N8N_RUNNERS_TASK_BROKER_URI: http://n8n-worker:5679
N8N_RUNNERS_AUTH_TOKEN: ${RUNNERS_AUTH_TOKEN}
N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT: "15"
depends_on:
- n8n-worker
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
depends_on:
- n8n
volumes:
postgres_data:
redis_data:
n8n_data:
caddy_data:
Notes:
- The
n8nio/runnersimage tag must match your n8n version. OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERSmakes even the test runs you start in the editor run on workers, so the runner sidecar on the worker handles all Code nodes.- Set
GENERIC_TIMEZONEto your timezone, or schedule triggers will fire at unexpected times.
Step 4: Add HTTPS with Caddy
cat > Caddyfile <<EOF
n8n.example.com {
reverse_proxy n8n:5678
}
EOF
docker compose up -d
docker compose ps
Caddy requests a free Let’s Encrypt certificate automatically. Open https://n8n.example.com, create the owner account, and enable two-factor authentication in your user settings right away.
Step 5: Scale with more workers
When executions start waiting in the queue, add workers. Each worker needs its own task runner sidecar pointing to it, so duplicate the n8n-worker and task-runners pair (for example n8n-worker-2 with a runner whose broker URI is http://n8n-worker-2:5679). For very high webhook traffic you can also run dedicated n8n webhook processors behind your load balancer.
Step 6: Back up n8n correctly
A complete n8n backup has three parts. Missing any of them makes a restore fail:
- The PostgreSQL database (workflows, credentials, executions).
- The encryption key (
N8N_ENCRYPTION_KEYin your.env). - Your config files (
docker-compose.yml,.env,Caddyfile).
# Daily database dump (add to cron)
docker compose exec -T postgres pg_dump -U n8n -Fc n8n > backups/n8n-$(date +%F).dump
# Keep 14 days
find backups/ -name "n8n-*.dump" -mtime +14 -delete
Copy backups to another location (object storage or a second server). A backup that lives only on the same disk is not a backup. Test a restore at least once.
Step 7: Update n8n safely
# 1. Back up first
# 2. Read the release notes for breaking changes
# 3. Change N8N_VERSION in .env, then:
docker compose pull
docker compose up -d
docker compose logs -f n8n
n8n, the workers and the task runners must all run the same version, which is why this setup uses one N8N_VERSION variable for all of them.
Security hardening checklist
- Expose only ports 80/443 and SSH. Never publish 5678, 5432 or 6379.
- Use SSH keys and disable password login for root.
- Enable 2FA for every n8n user, and SSO if you have a license.
- Keep
N8N_BLOCK_ENV_ACCESS_IN_NODE=trueso Code nodes cannot read your secrets. - Protect production webhooks with header auth or a secret path.
- Install only trusted community nodes. They run with access to your instance.
- Patch n8n and the operating system regularly.
- Put the server behind a WAF or CDN if your webhooks are public.
Monitoring
- Use the health endpoint
/healthzwith an uptime monitor. - Watch disk usage: execution data and logs grow quickly. Prune execution data regularly.
- Set up an error workflow in n8n (Settings → Error workflow) that alerts you on Slack, Telegram or email when a production workflow fails.
Common self-hosting mistakes
| Symptom | Cause | Fix |
|---|---|---|
Webhook URLs show localhost:5678 |
Public URL not set | Set N8N_WEBHOOK_URL (2.30+) or WEBHOOK_URL |
| Credentials fail after migration | Different encryption key | Restore the original N8N_ENCRYPTION_KEY |
| Schedules run at the wrong time | Timezone not set | Set GENERIC_TIMEZONE |
| Code nodes fail or time out | Task runner missing or wrong version | Run n8nio/runners with the same tag as n8n |
| Editor slow during heavy runs | Everything runs in the main process | Enable queue mode and add workers |
| Database grows to many GB | Old execution data never pruned | Enable pruning and a retention period |
| Wrong client IPs / rate limits | Proxy hops not configured | Set N8N_PROXY_HOPS=1 behind one proxy |
Self-hosted vs managed n8n
| Self-hosted | Managed (n8nhost.io) | |
|---|---|---|
| Executions | Unlimited | Unlimited |
| Community nodes | Yes | Yes |
| Setup | You (hours) | Ready in minutes |
| SSL, updates, backups | You | Included |
| Price | Server + your time | From $4.99/month |
Self-hosting makes sense when you enjoy running infrastructure. If you would rather spend that time building automations, try n8nhost.io free for 14 days.
Frequently asked questions
Is self-hosted n8n really free?
The Community Edition costs nothing and has no execution limits. You pay for the server and spend time on maintenance. Some advanced features require a paid license key.
Can I self-host n8n without Docker?
Yes, with npm and Node.js, but n8n recommends Docker Compose for production and is phasing out the npm method. Docker also makes updates and rollbacks much easier.
Do I need Redis?
Only for queue mode. A small instance can run without Redis. Use Redis and workers once you have many concurrent executions.
What are n8n task runners?
Task runners execute the JavaScript and Python inside Code nodes in a separate, isolated container (n8nio/runners). External mode is recommended for production, and the runner version must match n8n.
How do I move my self-hosted n8n to another server?
Restore the PostgreSQL dump on the new server, use the same N8N_ENCRYPTION_KEY, update DNS, and start the stack. Without the original key, saved credentials cannot be decrypted.
Can I put n8n behind Cloudflare?
Yes. Proxy the domain through Cloudflare, use “Full (strict)” SSL mode, and set N8N_PROXY_HOPS to match the number of proxies in front of n8n.
