n8n Docker Setup: The Complete Guide to Self-Hosting n8n with Docker
Docker is the fastest path to a production-ready n8n deployment. Skip the dependency nightmares, environment conflicts, and manual configuration headaches. With a single command, you get a fully isolated n8n instance running on any server.
Most tutorials stop at the basic docker run command. That works for testing but falls apart in production. This guide covers everything: from your first container to a hardened, scalable deployment with PostgreSQL, automatic SSL, and proper monitoring.
The Docker Advantage
Running n8n in Docker provides isolation that native installations cannot match. Your automation platform stays separate from the host system. Dependencies never conflict. Updates become trivial. Rollbacks take seconds instead of hours.
Docker also makes your deployment portable. The same configuration runs identically on your laptop, a $5 VPS, or enterprise Kubernetes. No more “works on my machine” problems.
For organizations weighing self-hosted versus n8n Cloud, Docker dramatically reduces the operational complexity argument. A properly containerized n8n instance requires minimal ongoing maintenance while giving you complete data control and significant cost savings at scale.
What You’ll Learn
- Quick Start: Run n8n in Docker with one command
- Production Setup: Docker Compose with PostgreSQL and health checks
- Environment Variables: Complete reference for configuration
- SSL/HTTPS: Automatic certificates with Traefik or Nginx
- Troubleshooting: Fix common Docker-specific issues
- Upgrades: Update n8n without losing data
- Security: Harden your deployment against attacks
Prerequisites
Before deploying n8n, ensure your system meets these requirements.
Docker and Docker Compose
You need Docker Engine version 20.10 or later. Docker Compose comes bundled with modern Docker installations.
Verify your installation:
docker --version
docker compose version
Both commands should return version numbers. If docker compose fails, you may have the older standalone version. Install the plugin or use docker-compose (with hyphen) instead.
Hardware Recommendations
| Workload | CPU | RAM | Storage |
|---|---|---|---|
| Testing/Development | 1 core | 1 GB | 10 GB |
| Light Production | 2 cores | 2 GB | 20 GB |
| Standard Production | 2 cores | 4 GB | 50 GB |
| Heavy Workflows | 4+ cores | 8+ GB | 100+ GB |
For workflows processing large files, complex JSON, or AI operations, lean toward higher specs. Running out of memory crashes your instance and corrupts execution data.
Quick Start: Basic Docker Setup
Get n8n running in under two minutes. This setup uses SQLite and stores data in a Docker volume.
Create a Volume and Run n8n
docker volume create n8n_data
docker run -d \
--name n8n \
-p 5678:5678 \
-e GENERIC_TIMEZONE="America/New_York" \
-e TZ="America/New_York" \
-v n8n_data:/home/node/.n8n \
--restart unless-stopped \
docker.n8n.io/n8nio/n8n
Replace America/New_York with your timezone. The --restart unless-stopped flag ensures n8n starts automatically after reboots.
Access the Web Interface
Open http://localhost:5678 in your browser. You’ll see the n8n setup wizard prompting you to create an owner account.
Note: This basic setup works for testing and personal use. For anything beyond experimentation, use the Docker Compose setup below with PostgreSQL.
Understanding the Volume Mount
The -v n8n_data:/home/node/.n8n flag is critical. Without it, your workflows, credentials, and execution history disappear when the container restarts.
The volume stores:
- Workflow definitions
- Encrypted credentials
- Execution logs
- Encryption keys
- Configuration settings
Never skip the volume mount. We’ve seen users lose months of work from this single oversight. Our self-hosting mistakes guide covers more pitfalls like this.
Production Setup with Docker Compose
For production deployments, Docker Compose orchestrates multiple services: n8n, PostgreSQL for the database, and optionally a reverse proxy for SSL.
Project Structure
Create a dedicated directory for your n8n deployment:
mkdir n8n-docker && cd n8n-docker
You’ll create two files: docker-compose.yml and .env.
Docker Compose Configuration
Create docker-compose.yml with this production-ready configuration:
services:
postgres:
image: postgres:15-alpine
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
ports:
- "5678:5678"
environment:
# Database Configuration
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
# Security
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
# Instance Settings
- N8N_HOST=${N8N_HOST}
- N8N_PORT=5678
- N8N_PROTOCOL=${N8N_PROTOCOL}
- WEBHOOK_URL=${WEBHOOK_URL}
# Timezone
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
# Performance
- N8N_RUNNERS_ENABLED=true
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=168
- EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
volumes:
- n8n_data:/home/node/.n8n
- ./local-files:/files
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
n8n_data:
Environment File
Create .env in the same directory:
# PostgreSQL
POSTGRES_USER=n8n
POSTGRES_PASSWORD=your-secure-database-password
POSTGRES_DB=n8n
# n8n Configuration
N8N_ENCRYPTION_KEY=your-32-character-encryption-key-here
N8N_HOST=localhost
N8N_PROTOCOL=http
WEBHOOK_URL=http://localhost:5678/
GENERIC_TIMEZONE=America/New_York
Generate a secure encryption key:
openssl rand -hex 32
Copy the output to N8N_ENCRYPTION_KEY. This key encrypts credentials in the database. Lose it, and you lose access to all stored credentials.
Critical: Back up your encryption key separately from your database backups. Store it in a password manager or secrets vault.
Start the Stack
docker compose up -d
Check that both services are healthy:
docker compose ps
You should see both postgres and n8n with status “Up” and the postgres healthcheck showing “healthy.”
View Logs
Monitor startup and catch errors:
# All services
docker compose logs -f
# Just n8n
docker compose logs -f n8n
Why PostgreSQL Over SQLite
The default SQLite database stores everything in a single file. It works for testing but causes problems at scale:
- Concurrent access fails: Multiple workflow executions corrupt the database
- No replication: Your data exists in one place with zero redundancy
- Performance degrades: Large execution histories slow everything down
- Backups are risky: You cannot safely back up while n8n is running
PostgreSQL handles concurrent connections properly, supports point-in-time recovery, and scales with your workload. The extra setup takes 10 minutes and prevents catastrophic data loss.
For detailed database migration instructions, see our comprehensive self-hosting guide.
Essential Environment Variables
n8n’s behavior is controlled through environment variables. Here’s a complete reference for Docker deployments.
Core Configuration
| Variable | Description | Example |
|---|---|---|
N8N_HOST | Hostname for the n8n instance | n8n.example.com |
N8N_PORT | Port n8n listens on | 5678 |
N8N_PROTOCOL | Protocol (http or https) | https |
WEBHOOK_URL | Public URL for webhooks | https://n8n.example.com/ |
N8N_ENCRYPTION_KEY | 32-char key for credential encryption | abc123... |
GENERIC_TIMEZONE | Default timezone | America/New_York |
Database Settings
| Variable | Description | Default |
|---|---|---|
DB_TYPE | Database type | sqlite |
DB_POSTGRESDB_HOST | PostgreSQL hostname | - |
DB_POSTGRESDB_PORT | PostgreSQL port | 5432 |
DB_POSTGRESDB_DATABASE | Database name | - |
DB_POSTGRESDB_USER | Database user | - |
DB_POSTGRESDB_PASSWORD | Database password | - |
Execution Settings
| Variable | Description | Default |
|---|---|---|
EXECUTIONS_DATA_PRUNE | Auto-delete old executions | false |
EXECUTIONS_DATA_MAX_AGE | Hours to keep executions | 336 |
EXECUTIONS_DATA_PRUNE_MAX_COUNT | Max executions to keep | 10000 |
EXECUTIONS_DATA_SAVE_ON_ERROR | Save failed executions | all |
EXECUTIONS_DATA_SAVE_ON_SUCCESS | Save successful executions | all |
N8N_CONCURRENCY_PRODUCTION_LIMIT | Max concurrent executions | -1 (unlimited) |
Performance Tuning
| Variable | Description | Recommendation |
|---|---|---|
N8N_RUNNERS_ENABLED | Enable task runners | true |
NODE_OPTIONS | Node.js memory settings | --max-old-space-size=2048 |
N8N_PAYLOAD_SIZE_MAX | Max request body size | 16 (MB) |
For high-volume deployments, enable queue mode to distribute execution across multiple workers.
Security Settings
| Variable | Description | Production Value |
|---|---|---|
N8N_BASIC_AUTH_ACTIVE | Enable basic auth | true |
N8N_BASIC_AUTH_USER | Auth username | Your choice |
N8N_BASIC_AUTH_PASSWORD | Auth password | Strong password |
N8N_SECURE_COOKIE | Use secure cookies | true (with HTTPS) |
Important: Basic auth is deprecated in favor of n8n’s built-in user management. For new deployments, create an owner account through the setup wizard instead.
SSL/HTTPS Configuration
Production deployments require HTTPS. Exposing n8n over plain HTTP transmits credentials and session tokens in cleartext.
Option 1: Traefik (Recommended)
Traefik automatically obtains and renews SSL certificates from Let’s Encrypt. This is n8n’s officially recommended approach.
Update your docker-compose.yml:
services:
traefik:
image: traefik:v2.10
restart: always
command:
- "--api.insecure=false"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
- "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- traefik_data:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
labels:
- traefik.enable=true
- traefik.http.routers.n8n.rule=Host(`${N8N_HOST}`)
- traefik.http.routers.n8n.tls=true
- traefik.http.routers.n8n.entrypoints=web,websecure
- traefik.http.routers.n8n.tls.certresolver=mytlschallenge
- traefik.http.middlewares.n8n.headers.SSLRedirect=true
- traefik.http.middlewares.n8n.headers.STSSeconds=315360000
- traefik.http.middlewares.n8n.headers.browserXSSFilter=true
- traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
- traefik.http.middlewares.n8n.headers.forceSTSHeader=true
- traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.n8n.headers.STSPreload=true
- traefik.http.routers.n8n.middlewares=n8n@docker
environment:
- N8N_HOST=${N8N_HOST}
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://${N8N_HOST}/
# ... other environment variables
# Remove the ports section - Traefik handles routing
volumes:
traefik_data:
Add to your .env:
SSL_EMAIL=your-email@example.com
N8N_HOST=n8n.yourdomain.com
Option 2: Nginx Reverse Proxy
If you prefer Nginx or already have it running:
server {
listen 80;
server_name n8n.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name n8n.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
}
}
Use Certbot to obtain certificates:
certbot --nginx -d n8n.yourdomain.com
DNS Configuration
Point your domain’s A record to your server’s IP address. SSL certificate generation will fail if DNS is not properly configured.
Troubleshooting Common Issues
Docker deployments fail for predictable reasons. Here are the most common problems and their solutions.
Permission Denied Errors
Symptom: Container starts then immediately stops. Logs show permission errors.
Cause: n8n runs as user node (UID 1000). If Docker creates the volume directory with root ownership, n8n cannot write to it.
Fix:
# Find your volume location
docker volume inspect n8n_data
# Fix permissions (adjust path from inspect output)
sudo chown -R 1000:1000 /var/lib/docker/volumes/n8n_data/_data
For bind mounts instead of named volumes:
sudo chown -R 1000:1000 ./n8n_data
Port Already in Use
Symptom: Container fails to start with “port is already allocated” error.
Cause: Another application (or another n8n instance) is using port 5678.
Fix:
# Find what's using the port
lsof -i :5678
# Either stop that process or change n8n's port
# In docker-compose.yml:
ports:
- "5679:5678" # Map to different host port
Data Loss After Restart
Symptom: Workflows and credentials disappear when container restarts.
Cause: No persistent volume configured.
Fix: Always include a volume mount:
volumes:
- n8n_data:/home/node/.n8n
For existing containers, export your workflows first (Settings > Export), then recreate with proper volumes.
JavaScript Heap Out of Memory
Symptom: Container crashes with memory allocation errors during workflow execution.
Cause: Workflows processing large datasets exceed Node.js memory limits.
Fix: Increase memory allocation:
environment:
- NODE_OPTIONS=--max-old-space-size=4096
Also set container memory limits:
deploy:
resources:
limits:
memory: 4G
If memory issues persist, consider breaking large workflows into smaller sub-workflows or enabling queue mode for distributed execution.
Cannot Connect to PostgreSQL
Symptom: n8n fails to start with database connection errors.
Cause: PostgreSQL container not ready, wrong credentials, or network issues.
Fix:
- Verify PostgreSQL is running:
docker compose ps - Check credentials match in
.env - Ensure
depends_onwith health check is configured - Test connection manually:
docker compose exec postgres psql -U n8n -d n8n -c "SELECT 1"
Webhooks Not Triggering
Symptom: External services cannot reach your webhooks.
Cause: WEBHOOK_URL misconfigured, firewall blocking, or no public access.
Fix:
- Set
WEBHOOK_URLto your publicly accessible URL - Ensure port 443 (or 80) is open in your firewall
- Verify DNS resolves correctly
- Check SSL certificate is valid
For webhook debugging, use our workflow debugger tool.
OAuth Credentials Failing
Symptom: Google, Microsoft, or other OAuth integrations fail to authenticate.
Cause: Redirect URI mismatch between OAuth app configuration and n8n settings.
Fix:
- Set
N8N_HOSTandWEBHOOK_URLto your actual domain - In your OAuth provider (Google Cloud Console, Azure, etc.), add the redirect URI:
https://your-domain.com/rest/oauth2-credential/callback - Restart n8n after changing environment variables
For detailed OAuth troubleshooting, see our authentication errors guide.
Upgrading n8n in Docker
Regular updates bring security patches, bug fixes, and new features. Here’s how to upgrade safely.
Check Current Version
docker compose exec n8n n8n --version
Pull the Latest Image
docker compose pull n8n
Or specify a version:
docker pull docker.n8n.io/n8nio/n8n:1.70.0
Recreate the Container
docker compose up -d n8n
Docker Compose automatically stops the old container and starts a new one with the updated image. Your data persists in the volume.
Version Pinning
For production stability, pin to specific versions instead of latest:
services:
n8n:
image: docker.n8n.io/n8nio/n8n:1.70.0
This prevents unexpected changes from automatic pulls. Update deliberately after reviewing release notes.
Rollback if Needed
If an update causes problems:
# Stop the current container
docker compose down
# Change image version in docker-compose.yml to previous version
# Then start again
docker compose up -d
Your data remains intact because it lives in the volume, not the container.
Before Major Upgrades
- Back up your database:
docker compose exec postgres pg_dump -U n8n n8n > backup.sql - Export workflows: Download JSON exports from n8n’s UI
- Note your encryption key: You cannot recover credentials without it
- Test on staging first: Run the new version in a separate environment
Security Hardening
A default n8n installation is not secure. Apply these measures before exposing your instance to the internet.
Network Isolation
Keep database and internal services on a private network:
services:
postgres:
networks:
- internal
n8n:
networks:
- internal
- external
networks:
internal:
internal: true
external:
Only n8n connects to the external network. PostgreSQL remains inaccessible from outside.
Firewall Rules
On your host server, restrict access:
# Allow only necessary ports
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP (redirect to HTTPS)
ufw allow 443/tcp # HTTPS
ufw enable
Block direct access to port 5678 if using a reverse proxy.
Authentication
n8n’s built-in user management requires creating an owner account on first access. For additional protection, add HTTP basic auth at the reverse proxy level.
Never expose n8n without authentication. The editor interface provides full access to credentials, code execution, and system commands.
Credential Encryption
Always set N8N_ENCRYPTION_KEY:
N8N_ENCRYPTION_KEY=your-32-character-encryption-key
Without this, credentials are encrypted with a default key that anyone can find in the source code. For advanced credential security patterns, see our credential management guide.
Disable Unnecessary Features
For internal tools that don’t need public templates:
environment:
- N8N_TEMPLATES_ENABLED=false
- N8N_VERSION_NOTIFICATIONS_ENABLED=false
- N8N_DIAGNOSTICS_ENABLED=false
Security Headers
If using Nginx instead of Traefik, add security headers:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
For a complete security checklist, refer to our self-hosting guide.
Monitoring Your Deployment
Production instances need visibility. Know when things break before users report them.
Health Checks
Add a health check to your n8n service:
services:
n8n:
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:5678/healthz"]
interval: 30s
timeout: 10s
retries: 3
Enable Metrics
n8n exposes Prometheus-compatible metrics:
environment:
- N8N_METRICS=true
- N8N_METRICS_INCLUDE_DEFAULT_METRICS=true
- N8N_METRICS_INCLUDE_QUEUE_METRICS=true
Access metrics at http://your-n8n:5678/metrics.
Error Alerting
Create a workflow that triggers on execution failures and sends notifications to Slack, email, or your preferred channel. The Error Trigger node captures failures from any workflow.
Log Aggregation
Forward container logs to a central system:
docker compose logs -f | tee -a /var/log/n8n.log
Or configure Docker’s logging driver to ship to Datadog, Loki, or CloudWatch.
When to Go Beyond Single Instance
Docker handles most n8n workloads beautifully. Consider upgrading when you hit these limits:
Signs you need queue mode:
- Workflows timing out during high traffic
- Memory errors from concurrent executions
- UI becoming sluggish during execution spikes
Our queue mode guide covers multi-worker setups with Redis.
Signs you need Kubernetes:
- Managing multiple n8n instances across teams
- Auto-scaling requirements
- Complex networking and service mesh needs
Signs you need managed services:
- No DevOps expertise on the team
- Compliance requirements you cannot meet in-house
- Preference for operational simplicity over cost savings
If infrastructure management isn’t your core competency, our self-hosted setup service handles deployment, security, and ongoing maintenance.
Frequently Asked Questions
Can I run n8n Docker on Windows?
Yes. Install Docker Desktop for Windows and use WSL 2 backend for best performance. The Docker Compose configurations in this guide work identically on Windows. Avoid using Windows paths for bind mounts. Use named volumes instead for simpler permission handling.
How do I migrate from SQLite to PostgreSQL in Docker?
Export your workflows from n8n’s UI as JSON files. Create a new deployment with PostgreSQL configured. Import the workflows into the new instance and re-add your credentials. For complex migrations with many workflows, consider our setup service which includes database migration assistance.
What’s the best VPS provider for n8n Docker?
Any provider supporting Docker works: DigitalOcean, Hetzner, Linode, Vultr, AWS Lightsail. For cost-effective hosting, Hetzner offers excellent specs per dollar. Start with a $10-20/month instance (2 cores, 4GB RAM) and scale as needed. Avoid shared CPU instances for production workloads.
How do I back up my Docker n8n instance?
Back up three things: the PostgreSQL database, the n8n data volume, and your .env file. For PostgreSQL: docker compose exec postgres pg_dump -U n8n n8n > backup.sql. For the volume, copy the entire volume contents or use Docker’s volume backup commands. Automate this with cron and upload to S3 or similar. Test restores regularly.
Why are my workflows not persisting after container updates?
If you’re losing data, your volume mount is misconfigured. Ensure n8n_data:/home/node/.n8n appears in your compose file under the n8n service’s volumes. Named volumes (like n8n_data) persist across container recreation. If using bind mounts, check directory permissions allow UID 1000 to write.
Next Steps
You now have everything needed to deploy n8n with Docker. Start with the basic setup for testing, then graduate to the production Docker Compose configuration for real workloads.
Recommended reading:
- n8n Self-Hosting Guide for comprehensive deployment strategies
- Common Self-Hosting Mistakes to avoid costly errors
- Queue Mode Setup when you need to scale beyond a single instance
For the official Docker documentation, see the n8n Docker installation guide and the Docker Hub page.
If you’d rather skip the infrastructure work entirely, our n8n setup service delivers a production-ready deployment configured for your specific needs.