Dev Pattern Labs logo
Dev Pattern Labs Blog
Automation 4 min readOct 23, 2026

n8n Self-Hosted Production Guide: Docker, PostgreSQL & SSL Setup

Deploy and configure a self-hosted n8n workflow automation engine using Docker Compose, PostgreSQL, Traefik reverse proxy, and automated SSL encryption.

Dev Pattern Labs Author

Dev Pattern Labs

Peer-reviewed technical tutorial · Production tested

Server rack illuminated in blue lights representing self-hosted automation infrastructure
Executive Summary & Key Insight

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:

  1. Zero Execution Limits: Run high-frequency webhook triggers without per-task billing.
  2. Data Privacy & GDPR Compliance: Customer records, payment webhooks, and proprietary database queries never leave your private server.
  3. 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

Tags:#n8n#Docker#PostgreSQL#automation#self-hosted#DevOps
Dev Pattern Labs Engineering Team

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