Deploy and configure a self-hosted n8n workflow automation engine using Docker Compose, PostgreSQL, Traefik reverse proxy, and automated SSL encryption. This practical guide covers step-by-step setup, real-world code snippets, security hardening, and common production edge cases.
Executive Summary & Key Takeaway (AEO)
Self-hosting n8n eliminates expensive SaaS automation per-execution fees (such as Zapier and Make) while maintaining full data sovereignty. A production-ready setup requires replacing the default SQLite database with PostgreSQL, enabling webhook queue workers with Redis, running behind a reverse proxy (Traefik or Nginx) with automatic Let's Encrypt SSL certificates, and configuring persistent volume backups.
Why Self-Host n8n in Production?
While SaaS automation platforms charge steep surcharges when execution volume crosses 10,000 runs per month, an agency or enterprise running hundreds of daily webhook flows can run unlimited executions on a $10 to $20/month cloud VPS (Hetzner, DigitalOcean, or AWS EC2).
Key operational benefits include:
- Zero Execution Limits: Run high-frequency webhook triggers without per-task billing.
- Data Privacy & GDPR Compliance: Customer records, payment webhooks, and proprietary database queries never leave your private server.
- Custom npm & Python Packages: Install community libraries and execute custom CLI scripts directly inside automation nodes.
Production Architecture Overview
The recommended production deployment isolates state, worker processes, and incoming webhooks using Docker Compose:
flowchart TD
Client["Incoming Webhook / External API"] --> ReverseProxy["Traefik / Nginx (SSL Termination)"]
ReverseProxy --> n8nPrimary["n8n Webhook & UI Container"]
n8nPrimary <--> Redis["Redis (Task Queue Broker)"]
Redis <--> n8nWorker["n8n Dedicated Queue Worker"]
n8nPrimary <--> Postgres["PostgreSQL Database (Persistent Storage)"]
n8nWorker <--> Postgres
Step 1: Server Prerequisites & Environment Configuration
Provision an Ubuntu 24.04 LTS VPS with at least 2 vCPUs and 4 GB RAM. Install Docker and Docker Compose:
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl ufw apt-transport-https ca-certificates gnupg lsb-release
# Install Docker Engine
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
Create a dedicated application directory:
mkdir -p /opt/n8n && cd /opt/n8n
Create your production environment file .env:
# Domain & Host Configuration
DOMAIN_NAME=n8n.devpatternlabs.com
SUBDOMAIN=n8n
GENERIC_TIMEZONE=UTC
SSL_EMAIL=devpatternlabs@gmail.com
# PostgreSQL Credentials
POSTGRES_USER=n8n_db_user
POSTGRES_PASSWORD=SuperSecretProductionDbPassword2026!
POSTGRES_DB=n8n_production
POSTGRES_NON_ROOT_USER=n8n_user
POSTGRES_NON_ROOT_PASSWORD=N8nDedicatedUserPassword2026!
# n8n Security & Encryption Key (Generate via: openssl rand -hex 32)
N8N_ENCRYPTION_KEY=e4f8b92d6e3c1a7045b88219c6f0e34159d8721cba38e120f547c1b829d630ae
WEBHOOK_URL=https://n8n.devpatternlabs.com/
N8N_HOST=n8n.devpatternlabs.com
N8N_PORT=5678
N8N_PROTOCOL=https
NODE_ENV=production
Step 2: Docker Compose Configuration
Create /opt/n8n/docker-compose.yml defining PostgreSQL, n8n, and Traefik:
version: '3.8'
volumes:
db_storage:
n8n_storage:
traefik_certificates:
networks:
n8n_network:
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- db_storage:/var/lib/postgresql/data
networks:
- n8n_network
healthcheck:
test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 10
traefik:
image: traefik:v3.1
restart: unless-stopped
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.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=${SSL_EMAIL}"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- traefik_certificates:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- n8n_network
n8n:
image: docker.n8n.io/n8nio/n8n:latest
restart: unless-stopped
environment:
- 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}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_HOST=${N8N_HOST}
- N8N_PORT=${N8N_PORT}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- WEBHOOK_URL=${WEBHOOK_URL}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- EXECUTIONS_PROCESS=main
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`${N8N_HOST}`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.tls.certresolver=myresolver"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
volumes:
- n8n_storage:/home/node/.n8n
networks:
- n8n_network
depends_on:
postgres:
condition: service_healthy
Step 3: Launching & Verifying the Deployment
Start the stack in detached mode:
docker compose up -d
Verify service status:
docker compose ps
docker compose logs -f n8n
Navigate to https://n8n.devpatternlabs.com. Traefik will automatically negotiate an SSL certificate from Let's Encrypt within 30 seconds. Complete the initial Owner Account setup form.
Automated Backups to Prevent Data Loss
Configure an automated daily database dump via cron:
sudo crontab -e
Add the following command to backup PostgreSQL daily at 02:00 UTC:
0 2 * * * docker exec -t $(docker ps -qf "name=postgres") pg_dump -U n8n_db_user n8n_production | gzip > /opt/backups/n8n_$(date +\%F).sql.gz
Related Production Guides
Written by Dev Pattern LabsCore Engineering
Dev Pattern Labs is an engineering lab and digital consultancy specializing in custom Next.js web applications, WooCommerce REST API integrations, and workflow automation for growing businesses.
Recommended Companion Articles

Next.js 15 Stripe Subscriptions & Billing Customer Portal Guide
Build a SaaS subscription engine in Next.js 15 with Stripe Billing, Checkout Sessions, Customer Portal redirects, and webhook lifecycle event handlers.

Connecting n8n Workflows to Next.js API Routes via Secure Webhooks
Build two-way automation pipelines between self-hosted n8n and Next.js 15 Route Handlers. Covers mutual HMAC authentication, payload schemas, and retries.

Stripe Webhooks in Next.js 15: Payment Event Handling
Integrate Stripe webhooks with Next.js 15 App Router. Learn signature verification, raw body parsing, payment event handling, and Stripe CLI testing.