KhueApps
Home/AI Engineering/Self-Hosting n8n: Production-Ready Setup with Docker Compose

Self-Hosting n8n: Production-Ready Setup with Docker Compose

Last updated: October 06, 2025

Overview

n8n is a workflow automation tool useful in AI Engineering for orchestrating LLM calls, data pipelines, webhooks, and scheduled tasks. This guide shows how to self-host n8n reliably, from a minimal single-container setup to a production-ready stack with PostgreSQL, Redis, and workers.

Collection: Self-Hosting AI Models & Tools

Quickstart (Minimal, Single Container)

This gets you running locally or on a single VPS fast. It uses SQLite (embedded) and is ideal for testing or small personal setups.

Minimal working example

docker-compose.yml
version: "3.8"
services:
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    ports:
      - "5678:5678"
    environment:
      - NODE_ENV=production
      - GENERIC_TIMEZONE=UTC
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      - N8N_HOST=localhost
      - WEBHOOK_URL=http://localhost:5678/
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=change-me
      - N8N_ENCRYPTION_KEY=change-this-to-a-long-random-string
    volumes:
      - n8n_data:/home/node/.n8n
    restart: unless-stopped
volumes:
  n8n_data:

Steps:

  1. Save the file as docker-compose.yml.
  2. Start: docker compose up -d
  3. Open http://localhost:5678, log in with admin / change-me, and complete the initial setup.

Notes:

  • This uses SQLite stored under the n8n_data volume.
  • Use strong credentials and a strong 32+ character N8N_ENCRYPTION_KEY.

A tiny “Hello n8n” workflow (importable)

Import this JSON into n8n (Menu → Import from File) and click Execute to see a message.

{
  "name": "Hello n8n",
  "nodes": [
    {
      "parameters": {},
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [240, 300]
    },
    {
      "parameters": {
        "functionCode": "return [{ json: { message: 'Hello from self-hosted n8n' } }];"
      },
      "name": "Function",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [480, 300]
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          { "node": "Function", "type": "main", "index": 0 }
        ]
      ]
    }
  }
}

Production Setup (PostgreSQL + Redis + Workers)

For multi-user, higher reliability, and scaling concurrent executions, use Postgres for the database and Redis for queue-based execution.

docker-compose.yml
version: "3.8"
services:
  postgres:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=change-me
      - POSTGRES_DB=n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis_data:/data
    restart: unless-stopped

  n8n-web:
    image: n8nio/n8n:latest
    depends_on:
      - postgres
      - redis
    environment:
      - NODE_ENV=production
      - GENERIC_TIMEZONE=UTC
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - N8N_HOST=automation.example.com
      - WEBHOOK_URL=https://automation.example.com/
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=change-me
      - N8N_ENCRYPTION_KEY=change-this-to-a-long-random-string
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=change-me
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_BULL_REDIS_PORT=6379
    ports:
      - "5678:5678"
    volumes:
      - n8n_data:/home/node/.n8n
    restart: unless-stopped

  n8n-worker:
    image: n8nio/n8n:latest
    depends_on:
      - postgres
      - redis
    environment:
      - NODE_ENV=production
      - GENERIC_TIMEZONE=UTC
      - N8N_ENCRYPTION_KEY=change-this-to-a-long-random-string
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=change-me
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_BULL_REDIS_PORT=6379
      - N8N_ENABLE_EDITOR_UI=false
    command: n8n worker
    restart: unless-stopped

volumes:
  n8n_data:
  postgres_data:
  redis_data:

Numbered steps to launch:

  1. Set a DNS A/AAAA record for automation.example.com to your server.
  2. Replace credentials, N8N_ENCRYPTION_KEY, and domain in the compose file.
  3. Start: docker compose up -d
  4. Put n8n behind your HTTPS reverse proxy and ensure it forwards Host and X-Forwarded-* headers.
  5. Open https://automation.example.com and finish setup.

Key environment variables (essentials)

  • N8N_HOST: Public hostname (no scheme).
  • N8N_PORT/N8N_PROTOCOL: Port and scheme n8n listens on.
  • WEBHOOK_URL: Public base URL used in webhooks (must match your reverse proxy URL).
  • N8N_ENCRYPTION_KEY: Strong secret for credential encryption.
  • DB_TYPE: postgresdb for production; default is SQLite if omitted.
  • EXECUTIONS_MODE: queue for horizontal scaling with workers.
  • QUEUE_BULL_REDIS_HOST/PORT: Redis connection for queue mode.

Operating tips

  • Upgrades: docker compose pull && docker compose up -d (always back up first).
  • Backups: dump Postgres regularly and snapshot the n8n_data volume.
  • Logs: docker compose logs -f n8n-web n8n-worker
  • Scale workers: docker compose up -d --scale n8n-worker=3

Performance notes

  • Prefer Postgres over SQLite for reliability and concurrency.
  • Use EXECUTIONS_MODE=queue with Redis to run workflows on separate workers and scale horizontally.
  • For heavy tasks, run multiple workers and tune CPU/RAM accordingly; monitor DB and Redis.
  • Consider isolating executions into their own processes (set EXECUTIONS_PROCESS=own) if workflows are memory-heavy; this reduces impact on the main UI/webhook process.
  • Reduce logging in production (e.g., set N8N_LOG_LEVEL=warn) to lower I/O overhead.
  • Place n8n near your AI/LLM endpoints (same region) to reduce latency.

Common pitfalls

  • Missing WEBHOOK_URL or mismatched protocol/host breaks external triggers.
  • Using SQLite with multiple replicas or workers risks corruption—use Postgres.
  • No persistent volumes means losing credentials and workflows on container removal.
  • Weak or reused N8N_ENCRYPTION_KEY compromises stored credentials; rotate safely.
  • Reverse proxies that buffer or alter headers can delay or break webhooks; forward Host and X-Forwarded-Proto.
  • Large uploads may be blocked by your proxy; raise its request size limit if needed.

Minimal security checklist

  • Enable basic auth (already shown) and use strong passwords.
  • Restrict access to the admin UI to trusted networks if possible.
  • Keep Docker and images updated; patch Postgres and Redis.
  • Use HTTPS end-to-end; ensure reverse proxy enforces TLS.

Tiny FAQ

  • Can I use an external Postgres/Redis? Yes—point DB_POSTGRESDB_HOST and QUEUE_BULL_REDIS_HOST to those services.
  • Do I need Redis if I’m not scaling? No. For single-instance setups, Redis and queue mode are optional.
  • Will upgrades cause downtime? A brief restart occurs. Use multiple workers and a reverse proxy for minimal disruption.
  • Can n8n run offline? Yes, but nodes that call external APIs (e.g., LLMs) require internet access.
  • How do I change the port? Update N8N_PORT and the compose port mapping.

Next steps

  • Add authentication for webhooks where appropriate.
  • Create environment-specific credentials and use separate databases for staging/production.
  • Monitor with container metrics and Postgres/Redis health checks to anticipate bottlenecks.

Series: Self-Hosting AI Models & Tools

AI Engineering