Docker is the recommended way to deploy n8n, providing a consistent environment and easy updates. This guide covers everything from simple deployments to production-ready setups.
Quick Start
Simple Docker Run
The fastest way to get n8n running:
Create a Docker volume
docker volume create n8n_data
This persists your workflows, credentials, and settings between container restarts.
Run n8n container
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-v n8n_data:/home/node/.n8n \
ghcr.io/n8n-io/n8n
The /home/node/.n8n folder contains critical data including:
Encryption key for credentials
Webhook URLs
SQLite database (default)
User settings and configuration
Always persist this folder or your credentials cannot be decrypted after restart!
Docker Compose Deployments
Single Instance with PostgreSQL
A production-ready single instance setup:
version : '3.8'
volumes :
n8n_data :
postgres_data :
services :
postgres :
image : postgres:16-alpine
restart : unless-stopped
environment :
POSTGRES_DB : n8n
POSTGRES_USER : n8n
POSTGRES_PASSWORD : ${POSTGRES_PASSWORD}
volumes :
- postgres_data:/var/lib/postgresql/data
healthcheck :
test : [ 'CMD-SHELL' , 'pg_isready -U n8n' ]
interval : 5s
timeout : 5s
retries : 10
n8n :
image : ghcr.io/n8n-io/n8n:latest
restart : unless-stopped
ports :
- 5678:5678
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=${N8N_HOST}
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://${N8N_HOST}/
volumes :
- n8n_data:/home/node/.n8n
depends_on :
postgres :
condition : service_healthy
Generate a secure encryption key: Store this key securely! If lost, you cannot decrypt existing credentials.
With External Task Runners
Deploy n8n with isolated task runners for enhanced security:
version : '3.8'
volumes :
n8n_data :
postgres_data :
services :
postgres :
image : postgres:16-alpine
restart : unless-stopped
environment :
POSTGRES_DB : n8n
POSTGRES_USER : n8n
POSTGRES_PASSWORD : ${POSTGRES_PASSWORD}
volumes :
- postgres_data:/var/lib/postgresql/data
healthcheck :
test : [ 'CMD-SHELL' , 'pg_isready -U n8n' ]
interval : 5s
timeout : 5s
retries : 10
n8n :
image : ghcr.io/n8n-io/n8n:latest
restart : unless-stopped
ports :
- 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}
# Task Runner configuration
- N8N_RUNNERS_MODE=external
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
- N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
- N8N_RUNNERS_MAX_CONCURRENCY=10
volumes :
- n8n_data:/home/node/.n8n
depends_on :
postgres :
condition : service_healthy
n8n-runner :
image : ghcr.io/n8n-io/runners:latest
restart : unless-stopped
environment :
- N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
- N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
depends_on :
n8n :
condition : service_healthy
# Scale runners as needed
deploy :
replicas : 2
Queue Mode (Scaling Setup)
Deploy n8n in queue mode with Redis and multiple workers:
version : '3.8'
volumes :
n8n_data :
postgres_data :
redis_data :
services :
postgres :
image : postgres:16-alpine
restart : unless-stopped
environment :
POSTGRES_DB : n8n
POSTGRES_USER : n8n
POSTGRES_PASSWORD : ${POSTGRES_PASSWORD}
volumes :
- postgres_data:/var/lib/postgresql/data
healthcheck :
test : [ 'CMD-SHELL' , 'pg_isready -U n8n' ]
interval : 5s
timeout : 5s
retries : 10
redis :
image : redis:6.2.14-alpine
restart : unless-stopped
volumes :
- redis_data:/data
command : redis-server --appendonly yes
healthcheck :
test : [ 'CMD' , 'redis-cli' , 'ping' ]
interval : 5s
timeout : 3s
retries : 5
n8n :
image : ghcr.io/n8n-io/n8n:latest
restart : unless-stopped
ports :
- 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}
# Queue mode configuration
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- QUEUE_BULL_REDIS_DB=0
# Metrics
- N8N_METRICS=true
volumes :
- n8n_data:/home/node/.n8n
depends_on :
postgres :
condition : service_healthy
redis :
condition : service_healthy
n8n-worker :
image : ghcr.io/n8n-io/n8n:latest
restart : unless-stopped
command : worker
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}
# Queue mode configuration
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- QUEUE_HEALTH_CHECK_ACTIVE=true
# Worker concurrency
- N8N_CONCURRENCY_PRODUCTION_LIMIT=10
depends_on :
postgres :
condition : service_healthy
redis :
condition : service_healthy
# Scale workers as needed
deploy :
replicas : 2
Worker Scaling : Adjust deploy.replicas based on your execution volume. Monitor CPU and memory usage to determine the optimal number of workers.
Docker Image Options
n8n provides multiple Docker images:
Main Images
Task Runners
Custom Builds
ghcr.io/n8n-io/n8n Official n8n image from GitHub Container Registry. Tags:
latest - Latest stable release
1.x.x - Specific version (e.g., 1.25.0)
next - Latest unstable/beta release
# Pull latest stable
docker pull ghcr.io/n8n-io/n8n:latest
# Pull specific version
docker pull ghcr.io/n8n-io/n8n:1.25.0
# Pull next/beta
docker pull ghcr.io/n8n-io/n8n:next
ghcr.io/n8n-io/runners Isolated task runner image for external mode. docker pull ghcr.io/n8n-io/runners:latest
Use when:
Enhanced security isolation needed
Independent resource scaling required
Running untrusted workflow code
Building from Source Build n8n from source for custom modifications: # Clone repository
git clone https://github.com/n8n-io/n8n.git
cd n8n
# Build (includes compilation + Docker image)
pnpm build:docker
Building from source requires pre-compilation. See /home/daytona/workspace/source/docker/images/n8n/Dockerfile for details.
Environment Variables for Docker
Key environment variables for Docker deployments:
# PostgreSQL (recommended for production)
DB_TYPE = postgresdb
DB_POSTGRESDB_HOST = postgres
DB_POSTGRESDB_PORT = 5432
DB_POSTGRESDB_DATABASE = n8n
DB_POSTGRESDB_USER = n8n
DB_POSTGRESDB_PASSWORD = secure-password
DB_POSTGRESDB_SCHEMA = public
# SQLite (default, development only)
DB_TYPE = sqlite
DB_SQLITE_DATABASE = database.sqlite
EXECUTIONS_MODE = queue
QUEUE_BULL_REDIS_HOST = redis
QUEUE_BULL_REDIS_PORT = 6379
QUEUE_BULL_REDIS_DB = 0
QUEUE_BULL_REDIS_PASSWORD = redis-password # if using auth
QUEUE_HEALTH_CHECK_ACTIVE = true
# n8n main process
N8N_RUNNERS_MODE = external
N8N_RUNNERS_BROKER_LISTEN_ADDRESS = 0.0.0.0
N8N_RUNNERS_BROKER_PORT = 5679
N8N_RUNNERS_AUTH_TOKEN = your-secure-token
N8N_RUNNERS_MAX_CONCURRENCY = 10
# Runner process
N8N_RUNNERS_TASK_BROKER_URI = http://n8n:5679
N8N_RUNNERS_AUTH_TOKEN = your-secure-token
N8N_ENCRYPTION_KEY = your-secure-key-min-10-chars
N8N_HOST = your-domain.com
N8N_PROTOCOL = https
N8N_PORT = 5678
WEBHOOK_URL = https://your-domain.com/
Volume Mounts
Critical Data Locations The /home/node/.n8n directory contains:
config - Instance configuration
database.sqlite - Default database (if using SQLite)
.n8n.settings.json - Encryption key and settings
Always mount this directory to preserve data between restarts!
Recommended Mounts
volumes :
# Primary data directory (required)
- n8n_data:/home/node/.n8n
# Custom certificates (optional)
- ./custom-certs:/opt/custom-certificates:ro
# Custom nodes (optional)
- ./custom-nodes:/home/node/.n8n/custom
Updating n8n
Stop and remove old containers
This preserves your data volumes. Your workflows and credentials remain safe.
Verify the update
docker compose logs -f n8n
Check for successful startup and database migrations.
Before Updating:
Check Breaking Changes
Backup your database and encryption key
Test updates in a staging environment first
Plan for brief downtime during the update
Health Checks
n8n exposes a health check endpoint at /healthz:
healthcheck :
test : [ 'CMD-SHELL' , 'wget --spider -q http://localhost:5678/healthz || exit 1' ]
interval : 5s
timeout : 5s
retries : 10
start_period : 30s
The health endpoint checks:
Database connectivity
Redis connectivity (queue mode)
Application readiness
Returns HTTP 200 when healthy, 503 when unhealthy.
Timezone Configuration
Configure timezone for scheduled workflows:
# Set both for consistency
docker run \
-e GENERIC_TIMEZONE="Europe/Berlin" \
-e TZ="Europe/Berlin" \
ghcr.io/n8n-io/n8n
GENERIC_TIMEZONE - Used by n8n’s Schedule node
TZ - System timezone for container commands
Custom SSL Certificates
Trust custom SSL certificates:
services :
n8n :
volumes :
- ./custom-certs:/opt/custom-certificates:ro
environment :
- NODE_OPTIONS=--use-openssl-ca
- SSL_CERT_DIR=/opt/custom-certificates
The entrypoint script automatically runs c_rehash on the certificate directory.
Troubleshooting
# Check logs
docker compose logs -f n8n
# Common issues:
# 1. Port 5678 already in use
# 2. Database connection failed
# 3. Invalid encryption key
# 4. Insufficient permissions
Cannot decrypt credentials
This happens when:
Encryption key changed or lost
/home/node/.n8n volume not mounted
Different encryption key between containers
Solution: Ensure consistent N8N_ENCRYPTION_KEY and persistent volumes.
Workers not picking up jobs
# Verify Redis connection
docker compose exec redis redis-cli ping
# Check worker logs
docker compose logs -f n8n-worker
# Verify same encryption key and database
docker compose exec n8n env | grep N8N_ENCRYPTION_KEY
docker compose exec n8n-worker env | grep N8N_ENCRYPTION_KEY
# Set resource limits
services :
n8n :
deploy :
resources :
limits :
memory : 2G
reservations :
memory : 1G
Also consider:
Reducing N8N_CONCURRENCY_PRODUCTION_LIMIT
Adding more workers instead of increasing main process memory
Enabling execution data pruning
Next Steps
Configuration Deep dive into all configuration options
Scaling Learn about scaling with queue mode