How to Self-Host n8n: Complete Production Setup Guide
A step-by-step guide to deploying n8n in production with Docker, PostgreSQL, SSL, and security hardening — including cost comparisons with n8n Cloud and performance optimization tips for 2026.
How to Self-Host n8n: Complete Production Setup Guide
Self-hosting n8n gives you full control over your automation workflows, data residency, and costs — but a production deployment requires Docker, PostgreSQL, a reverse proxy with SSL, and proper security hardening. This guide walks through every step, from server provisioning to production-ready deployment, with current 2026 pricing and requirements.
Whether you are migrating from n8n Cloud or starting fresh, this guide covers the infrastructure sizing, Docker Compose configuration, database setup, SSL termination, security hardening, and backup strategies you need for a reliable self-hosted n8n instance.
What You Need Before You Start
A production n8n deployment requires four core components: a Linux server, a container runtime, a database, and a public domain with DNS configured.
System Requirements (2026)
The minimum and recommended specifications have shifted upward in 2025-2026 as n8n has added AI-heavy features and improved its concurrency model:
| Resource | Minimum (Testing) | Recommended (Production) | AI-Heavy Workloads |
|---|---|---|---|
| CPU | 2 vCPUs | 4 vCPUs | 8+ vCPUs |
| RAM | 2 GB | 8 GB | 16+ GB |
| Storage | 20 GB SSD | 80 GB NVMe SSD | 160+ GB NVMe |
| Database | SQLite | PostgreSQL 15+ | PostgreSQL 15+ |
| Runtime | Docker 24+ or Node.js 20.19+ | Docker 24+ | Docker 24+ + Redis 6+ |
Node.js 20.19 is now the baseline for self-hosting n8n — earlier guides referenced Node.js 16+, but the 2025-2026 updates have raised this requirement. Docker Engine 24.0+ with Docker Compose v2 is the standard deployment method.
Prerequisites Checklist
- An Ubuntu 22.04 or 24.04 VPS from a provider like Hetzner, DigitalOcean, or AWS
- A domain name (e.g.,
automation.yourcompany.com) with DNS A record pointing to your server IP - Docker Engine 24+ and Docker Compose v2 installed
- SSH key access to your server (password authentication should be disabled)
Step 1: Provision and Secure Your Server
Before installing n8n, harden your server. A surprising number of self-hosting failures stem from neglected server-level security rather than n8n configuration issues.
Basic Server Hardening
# Update the system
sudo apt update && sudo apt upgrade -y
# Create a non-root user
sudo adduser n8n
sudo usermod -aG sudo n8n
# Configure SSH key authentication and disable password login
sudo nano /etc/ssh/sshd_config
# Set: PasswordAuthentication no
sudo systemctl restart sshd
# Configure firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# Install Fail2Ban for brute-force protection
sudo apt install fail2ban -y
Only ports 22 (SSH), 80 (HTTP), and 443 (HTTPS) should be open. n8n runs on port 5678 internally but should never be exposed directly to the internet.
Step 2: Create the Docker Compose Stack
Create a directory structure and define your services. This setup uses PostgreSQL as the database — SQLite is fine for testing but causes concurrency and backup problems in production.
Directory Structure
mkdir -p ~/n8n-docker/{data,files,postgres-data}
cd ~/n8n-docker
docker-compose.yml
version: "3.8"
services:
n8n:
image: n8nio/n8n:1.65.0
restart: unless-stopped
environment:
N8N_HOST: automation.example.com
N8N_PORT: 5678
N8N_PROTOCOL: https
N8N_ENCRYPTION_KEY: "replace-with-a-32-char-random-string"
DB_TYPE: postgres
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: "replace-with-strong-db-password"
N8N_SECURE_COOKIE: "true"
N8N_DIAGNOSTICS_ENABLED: "false"
EXECUTIONS_DATA_PRUNE: "true"
EXECUTIONS_DATA_MAX_AGE: "168"
ports:
- "127.0.0.1:5678:5678"
volumes:
- ./data:/home/node/.n8n
- ./files:/files
depends_on:
- postgres
postgres:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: "replace-with-strong-db-password"
POSTGRES_DB: n8n
volumes:
- ./postgres-data:/var/lib/postgresql/data
Critical Configuration Choices
Pin your image version. Using n8nio/n8n:latest is one of the most common pitfalls in self-hosting n8n. Unpinned images can introduce breaking changes on every pull. Always pin to a specific version like 1.65.0 and upgrade deliberately after testing on a staging environment.
Use PostgreSQL, not SQLite. SQLite works for quick local tests but lacks the concurrency and reliability needed for production workflows. PostgreSQL 13-17 is the supported range, with version 15 being the most commonly recommended in 2026 guides. SQLite also complicates backups — you need to coordinate file copies carefully to avoid corruption, while PostgreSQL supports proper transactional backups.
Bind to localhost only. Note the port binding 127.0.0.1:5678:5678 — this ensures n8n is only accessible via the reverse proxy, not directly. This is a security best practice that prevents direct access to the n8n port from the internet.
Enable execution data pruning. The EXECUTIONS_DATA_PRUNE and EXECUTIONS_DATA_MAX_AGE settings automatically clean up execution logs older than 7 days (168 hours). Without this, your database will grow indefinitely and eventually degrade performance.
Generate a Strong Encryption Key
# Generate a random 32-character encryption key
openssl rand -hex 16
The N8N_ENCRYPTION_KEY encrypts stored credentials in your n8n database. If you lose this key, all stored credentials become unrecoverable. Store it securely — it is one of the most critical secrets in your deployment.
Step 3: Start the Stack
docker compose up -d
docker compose ps
docker compose logs -f n8n
Wait 2-3 minutes for n8n to fully initialize. You should see log output indicating the server is running on port 5678. At this point, n8n is running but only accessible locally — the next step is to add a reverse proxy for public access with SSL.
Step 4: Configure Nginx Reverse Proxy with SSL
A reverse proxy handles HTTPS termination, redirects HTTP to HTTPS, and provides security headers. Caddy is simpler (automatic HTTPS), but Nginx offers more granular control.
Install Nginx
sudo apt install nginx -y
Nginx Configuration
server {
listen 80;
server_name automation.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name automation.example.com;
ssl_certificate /etc/letsencrypt/live/automation.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/automation.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://127.0.0.1:5678;
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;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Obtain SSL Certificate with Let's Encrypt
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d automation.example.com
Certbot will automatically configure SSL and set up auto-renewal via a systemd timer. Verify the renewal is active:
sudo systemctl status certbot.timer
Step 5: Complete Initial Setup
Navigate to https://automation.example.com in your browser. You will see the n8n setup screen prompting you to create an owner account. Set a strong password and enable two-factor authentication immediately after account creation.
Post-Setup Configuration
- Enable 2FA: Navigate to Settings > Personal and enable two-factor authentication
- Configure email: Set up SMTP for execution alerts and password recovery via environment variables
- Review execution retention: Confirm your data pruning settings are active to prevent unbounded database growth
Self-Hosted n8n vs n8n Cloud: Cost Comparison
The cost equation depends heavily on your workload and whether you count engineering time.
Self-Hosted Costs (2026 Estimates)
| Component | Monthly Cost |
|---|---|
| VPS (4 vCPU, 8 GB RAM) | $20-$40 |
| Storage (80 GB NVMe + backups) | $5-$10 |
| Managed PostgreSQL (optional) | $15-$30 |
| Maintenance time (~2 hrs/month) | $200+ (opportunity cost) |
| Total (with labor) | $40-$280/month |
n8n Cloud Pricing (2026)
| Plan | Monthly Cost | Executions | Active Workflows |
|---|---|---|---|
| Starter | $19/month | 5,000 | 10 |
| Pro | $59/month | 25,000 | 40 |
When Self-Hosting Makes Sense
Self-hosting is cheaper at small scale — a Hetzner CX22 VPS costs around €4.35/month for 2 vCPU and 4 GB RAM, which is sufficient for personal or small-team use. However, once you need high availability, backups, monitoring, and staff time, self-hosting costs can reach or exceed the $59/month Pro plan.
The real value of self-hosting is not always cost savings. It is data residency control (keeping data in your jurisdiction), customization (adding custom nodes, modifying behavior), and no execution limits (you are constrained only by your hardware, not a credit system). If you run thousands of executions daily, self-hosting eliminates the per-execution cost that compounds quickly on Cloud plans.
PostgreSQL vs SQLite: Why It Matters
SQLite is n8n's default database for convenience, but every 2026 production guide recommends PostgreSQL. Here is why:
Concurrency: SQLite uses file-level locking, which means only one write operation can occur at a time. With multiple workflows executing simultaneously, this creates contention and delays. PostgreSQL handles concurrent connections efficiently.
Backups: SQLite backups require copying the entire database file, and if a workflow is executing during the copy, you risk capturing an inconsistent state. PostgreSQL supports online backups with tools like pg_dump and point-in-time recovery without stopping n8n.
Scalability: If you later want to run multiple n8n instances behind a load balancer (queue mode), PostgreSQL is a requirement. SQLite cannot be shared across instances.
Migration path: Moving from SQLite to PostgreSQL is possible but involves export-import steps. Starting with PostgreSQL from day one avoids this migration headache entirely.
Performance Optimization for Production
Once your instance is running, several optimizations can significantly improve throughput and reliability:
Enable queue mode with Redis. For high-volume deployments, queue mode separates workflow execution into worker processes coordinated via Redis 6+. This allows horizontal scaling — add more n8n worker containers as your workload grows.
Right-size your infrastructure. AI-heavy workflows that call LLM APIs and process large datasets need significantly more resources. Monitor CPU, RAM, and disk I/O, and scale vertically before adding complexity.
Modularize workflows. Break large monolithic workflows into smaller, reusable sub-workflows. This improves parallelism, reduces memory pressure, and makes debugging easier when something fails.
Implement retry logic. Use n8n's built-in retry nodes with exponential backoff instead of tight polling loops. This reduces unnecessary API calls and prevents cascading failures when an external service is temporarily unavailable.
Backup and Disaster Recovery
Your workflows and stored credentials are the most valuable assets in your n8n deployment. The application itself is easy to reinstall — but losing your database means losing everything.
What to Back Up
- PostgreSQL data: Use
pg_dumpfor logical backups or managed DB automated snapshots - n8n application data directory:
/home/node/.n8n(mounted to./datain our compose file), which may contain local files and configuration - Docker Compose file and environment variables: Store your
docker-compose.ymland a secure backup of all environment variables (especiallyN8N_ENCRYPTION_KEY)
Backup Script Example
#!/bin/bash
# Run via cron daily
BACKUP_DIR="/backups/n8n/$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
# PostgreSQL backup
docker exec n8n-docker-postgres-1 pg_dump -U n8n n8n > "$BACKUP_DIR/n8n-db.sql"
# Application data backup
tar czf "$BACKUP_DIR/n8n-data.tar.gz" -C ~/n8n-docker data
# Retain 30 days of backups
find /backups/n8n -maxdepth 1 -type d -mtime +30 -exec rm -rf {} +
# Upload to offsite storage (S3, Backblaze, etc.)
# aws s3 sync "$BACKUP_DIR" "s3://your-bucket/n8n-backups/$(date +%Y%m%d)/"
Test your restore process regularly — a backup you have never restored is not a backup you can rely on.
Common Pitfalls and How to Avoid Them
-
Using
:latestimage tag: Pin specific versions and upgrade deliberately. Unpinned images introduce breaking changes without warning. -
Exposing port 5678 directly: Always use a reverse proxy. Direct exposure increases your attack surface and bypasses SSL.
-
No backup strategy: Set up automated daily backups and test restores. Data loss in production n8n means losing complex workflows that took hours to build.
-
Under-provisioned hardware: 1 vCPU and 1 GB RAM will start n8n but workflows will stall under real load. Start at 4 vCPU and 8 GB RAM for production.
-
Losing the encryption key: The
N8N_ENCRYPTION_KEYis irreplaceable. Store it in a password manager or secrets vault alongside your database credentials.
Conclusion
Self-hosting n8n in production is straightforward with Docker and PostgreSQL, but it requires attention to security, backup, and performance from day one. The infrastructure cost ranges from under $10/month for personal use to $40-280/month for production deployments with managed services — but the real value is control over your data, no execution limits, and the ability to customize your automation stack.
For businesses that need help setting up or optimizing their n8n deployment, ishchuk.eu offers AI automation consulting services, including n8n implementation, workflow design, and production hardening.
Further Reading
Frequently asked questions
- How much does it cost to self-host n8n?
- Self-hosting n8n costs between $5-10 per month for a basic VPS suitable for personal use, and $40-280 per month for a production deployment with managed PostgreSQL, backups, and maintenance time. The base n8n software is free under its fair-code license, so the only costs are infrastructure and your time. For comparison, n8n Cloud starts at $19/month for 5,000 executions.
- What are the system requirements for self-hosting n8n in 2026?
- The minimum requirements are 2 vCPUs, 2 GB RAM, 20 GB SSD, and either Docker 24+ or Node.js 20.19+. For production, 4 vCPUs, 8 GB RAM, 80 GB NVMe SSD, and PostgreSQL 15+ are recommended. AI-heavy workloads with large language model integrations may need 8+ vCPUs and 16+ GB RAM.
- Should I use SQLite or PostgreSQL when self-hosting n8n?
- PostgreSQL is strongly recommended for any production n8n deployment. SQLite works for quick testing but lacks the concurrency handling, reliable backup mechanisms, and multi-instance scalability that PostgreSQL provides. SQLite uses file-level locking that creates contention with simultaneous workflows, while PostgreSQL handles concurrent connections efficiently and supports online backups.
- How do I secure a self-hosted n8n deployment?
- Secure your n8n deployment by using a reverse proxy like Nginx or Caddy with Let's Encrypt SSL, binding n8n to localhost only, enabling secure cookies with N8N_SECURE_COOKIE=true, using SSH key authentication on your server, configuring a firewall with UFW to only allow ports 22, 80, and 443, installing Fail2Ban, and enabling two-factor authentication in n8n. Always pin your Docker image version and keep all components updated.
- How do I back up a self-hosted n8n instance?
- Back up your PostgreSQL database using pg_dump or managed database snapshots, back up the n8n application data directory at /home/node/.n8n, and securely store your N8N_ENCRYPTION_KEY since it is required to decrypt stored credentials. Run backups daily via cron, retain at least 30 days, upload to offsite storage, and test your restore process regularly.
- What is the N8N_ENCRYPTION_KEY and why is it important?
- The N8N_ENCRYPTION_KEY is a secret string that encrypts all stored credentials in your n8n database, including API keys, passwords, and connection tokens. If you lose this key, all stored credentials become permanently unrecoverable. Generate it using a command like openssl rand -hex 16 and store it in a password manager or secrets vault alongside your database credentials.