A self-hosted n8n dedicated server setup runs n8n's workflow automation engine on hardware you fully control, paired with PostgreSQL for data storage, Redis for queue-based execution, and NGINX for encrypted traffic. It replaces per-task billing with a fixed monthly server cost and gives you unlimited workflow executions on the Community Edition.
This guide walks through the exact commands and configuration files needed to deploy n8n docker compose in a production-ready, scalable state.
Introduction: The Real Cost of Cloud Automation
If you've priced out Zapier or Make for a business running more than a handful of daily automations, you already know the task-based billing model gets expensive fast. Zapier's Professional plan starts around $29.99/month for 750 tasks, and its Team plan runs about $103.50/month for 2,000 pooled tasks. Overages are billed at roughly 1.25x your base task rate, so a busy month can push the bill well past the sticker price.
A self-hosted n8n instance on a dedicated server flips that math. You pay a flat monthly rate for the server, and every workflow execution is included—n8n's Community Edition is free and open-source, with no execution caps.
| Feature | Zapier Professional | Zapier Team | Self-Hosted n8n |
|---|---|---|---|
| Monthly cost | ~$29.99 | ~$103.50 | ~$20–$50 (flat server cost) |
| Task limit | 750 tasks | 2,000 tasks | Unlimited executions |
| Overage billing | ~1.25x base rate | ~1.25x base rate | None — fixed cost |
| Data ownership | Zapier's cloud | Zapier's cloud | Your infrastructure |
| Scaling model | Buy a higher tier | Buy a higher tier | Add worker containers |
The trade-off is obvious: self-hosting shifts the responsibility for uptime, security, and backups onto you. That's exactly what the rest of this guide is designed to handle properly.
Architecture Overview: Moving Past SQLite
What is n8n queue mode? It's an execution architecture where a main n8n process handles the UI, API, and webhook triggers, while separate worker containers pick up and run the actual workflow executions from a Redis-backed queue. This decouples "receiving a trigger" from "doing the work," which makes horizontal scaling possible.
Why the Default SQLite Setup Fails in Production
- Concurrency issues: SQLite locks the database file during writes, so simultaneous webhook executions can queue up or fail outright.
- No queue mode support: n8n's own documentation states that running a distributed setup with SQLite isn't supported—queue mode requires PostgreSQL.
- Scaling limits: A single SQLite file can't be shared reliably across multiple worker containers.
The Production Stack
- n8n (main process): Handles the editor UI, REST API, and incoming webhook requests.
- PostgreSQL: Stores workflows, credentials, and execution data (n8n recommends Postgres 13+).
- Redis: Acts as the message broker for queue mode.
- n8n (worker processes): Dedicated containers that run workflow executions.
- NGINX + Certbot: Terminates HTTPS traffic (mandatory for webhooks).
Prerequisites & Hardware Sizing
Before you start, you need:
- A dedicated server running Ubuntu 22.04 LTS or 24.04 LTS with root or sudo access.
- A registered domain name pointed at your server's IP address.
- Basic terminal comfort.
Hardware Guidelines (NVMe storage recommended):
| Use Case | vCPU | RAM | Notes |
|---|---|---|---|
| Testing | 1–2 | 2 GB | Minimum only; unstable under load |
| Small Business | 2–4 | 4 GB | Reasonable starting point for this stack |
| Growing Team | 4 | 8 GB | Recommended for worker replicas |
| AI / High-volume | 8+ | 16 GB+ | Needed for LLM chains or large payloads |
Step 1: Preparing the Server and Installing Docker
Connect to your server via SSH, update the system, and install Docker.
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install prerequisites
sudo apt install -y ca-certificates curl gnupg
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) | sudo gpg --deararm -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the Docker repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine and Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Verify the installation
docker compose version
# Allow your user to run Docker without sudo (log out and back in after this)
sudo usermod -aG docker $USER
Step 2: The Production Docker Compose File
Create a project directory and the configuration files.
mkdir -p ~/n8n-production && cd ~/n8n-production
Create your .env file:
# ---- Domain & timezone ----
DOMAIN_NAME=n8n.yourdomain.com
GENERIC_TIMEZONE=UTC
# ---- PostgreSQL ----
POSTGRES_USER=n8n_admin
POSTGRES_PASSWORD=CHANGE_THIS_STRONG_PASSWORD
POSTGRES_DB=n8n
POSTGRES_NON_ROOT_USER=n8n_app
POSTGRES_NON_ROOT_PASSWORD=CHANGE_THIS_TOO
# ---- n8n security ----
# Generate with: openssl rand -hex 32
N8N_ENCRYPTION_KEY=REPLACE_WITH_GENERATED_64_CHAR_KEY
# ---- Execution data retention ----
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=336
Create docker-compose.yml:
version: "3.8"
volumes:
db_storage:
n8n_storage:
redis_storage:
x-shared-n8n: &shared-n8n
image: docker.n8n.io/n8nio/n8n:latest
restart: always
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_HEALTH_CHECK_ACTIVE=true
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_HOST=${DOMAIN_NAME}
- N8N_PROTOCOL=https
- N8N_PORT=5678
- WEBHOOK_URL=https://${DOMAIN_NAME}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
- EXECUTIONS_DATA_PRUNE=${EXECUTIONS_DATA_PRUNE}
- EXECUTIONS_DATA_MAX_AGE=${EXECUTIONS_DATA_MAX_AGE}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
services:
postgres:
image: postgres:16-alpine
restart: always
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_NON_ROOT_USER=${POSTGRES_NON_ROOT_USER}
- POSTGRES_NON_ROOT_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
volumes:
- db_storage:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
redis:
image: redis:7-alpine
restart: always
volumes:
- redis_storage:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 10
n8n-main:
<<: *shared-n8n
container_name: n8n_main
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n_storage:/home/node/.n8n
command: start
n8n-worker:
<<: *shared-n8n
container_name: n8n_worker
volumes:
- n8n_storage:/home/node/.n8n
command: worker
deploy:
replicas: 2
Step 3: Securing with NGINX and SSL
Webhooks will silently fail without HTTPS. Install NGINX and Certbot on the host:
sudo apt install -y nginx certbot python3-certbot-nginx
Create the NGINX server block:
sudo nano /etc/nginx/sites-available/n8n.yourdomain.com
Paste the following config:
server {
listen 80;
server_name n8n.yourdomain.com;
location / {
proxy_pass [http://127.0.0.1:5678](http://127.0.0.1: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;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
Enable the site and generate the SSL certificate:
sudo ln -s /etc/nginx/sites-available/n8n.yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d n8n.yourdomain.com
Step 4: Booting Up
Bring the stack up:
docker compose up -d
docker compose ps
Visit https://n8n.yourdomain.com to create your owner account and start building.
Step 5: Disaster Recovery and Backups
You need backups of two things: your PostgreSQL database and your .env file (which contains your irreplaceable N8N_ENCRYPTION_KEY).
Create a backup script:
sudo nano /opt/n8n-backup.sh
#!/bin/bash
# n8n PostgreSQL + .env backup script
BACKUP_DIR="/opt/n8n-backups"
DATE=$(date +%F_%H%M)
PROJECT_DIR="/home/$(whoami)/n8n-production"
mkdir -p "$BACKUP_DIR"
# Dump the PostgreSQL database from inside the container
docker exec $(docker compose -f "$PROJECT_DIR/docker-compose.yml" ps -q postgres) \
pg_dump -U n8n_admin -d n8n > "$BACKUP_DIR/n8n_db_$DATE.sql"
# Back up the .env file
cp "$PROJECT_DIR/.env" "$BACKUP_DIR/env_$DATE.bak"
# Remove backups older than 14 days
find "$BACKUP_DIR" -type f -mtime +14 -delete
Make it executable and run it via cron daily at 3 AM:
sudo chmod +x /opt/n8n-backup.sh
crontab -e
Add line:
0 3 * * * /opt/n8n-backup.sh
Note: Always store a copy of your
.envfile off-server in a secure password manager.
Ready to deploy? Everything in this guide requires a server with predictable performance and zero noisy neighbors. Explore BytesRack Dedicated Servers to get a bare-metal machine sized perfectly for a production n8n stack with NVMe storage as standard.
Top comments (0)