n8n-self-host

SKILL.md

n8n Self-Host

Overview

Install, configure, scale, and operate self-hosted n8n instances. Covers Docker Compose production setups, CLI administration, REST API automation, queue-mode scaling with Redis workers, backup/restore, monitoring, and troubleshooting. For building n8n workflows and configuring nodes, see the n8n-workflow skill.

Instructions

Task A: Production Docker Compose Setup

Basic setup with PostgreSQL (recommended over default SQLite):

# docker-compose.yml
services:
  postgres:
    image: postgres:16
    restart: always
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    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

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: always
    ports:
      - "5678:5678"
    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=${ENCRYPTION_KEY}
      - WEBHOOK_URL=${WEBHOOK_URL}
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE:-UTC}
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_RUNNERS_ENABLED=true
    volumes:
      - n8n_storage:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  db_storage:
  n8n_storage:

Create a .env alongside with POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, ENCRYPTION_KEY (generate with openssl rand -hex 24), WEBHOOK_URL, and GENERIC_TIMEZONE. Start with docker compose up -d.

Task B: Scale with Queue Mode (Redis + Workers)

For high-volume workloads, add Redis and worker processes:

# Add to docker-compose.yml
x-n8n-shared: &n8n-shared
  image: docker.n8n.io/n8nio/n8n
  restart: always
  environment:
    - DB_TYPE=postgresdb
    - DB_POSTGRESDB_HOST=postgres
    - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
    - DB_POSTGRESDB_USER=${POSTGRES_USER}
    - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
    - EXECUTIONS_MODE=queue
    - QUEUE_BULL_REDIS_HOST=redis
    - QUEUE_HEALTH_CHECK_ACTIVE=true
    - N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}
  volumes:
    - n8n_storage:/home/node/.n8n
  depends_on:
    postgres:
      condition: service_healthy
    redis:
      condition: service_healthy

services:
  redis:
    image: redis:7-alpine
    restart: always
    volumes:
      - redis_storage:/data
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s

  n8n:
    <<: *n8n-shared
    ports:
      - "5678:5678"

  n8n-worker:
    <<: *n8n-shared
    command: worker
    # Scale with: docker compose up -d --scale n8n-worker=3

Task C: CLI Administration

Run CLI commands inside Docker: docker exec -u node -it n8n n8n <command>.

# Export/import workflows
n8n export:workflow --backup --output=backups/
n8n export:workflow --id=<ID> --output=workflow.json
n8n import:workflow --input=workflow.json
n8n import:workflow --separate --input=backups/

# Export/import credentials (--decrypted for cross-instance migration)
n8n export:credentials --backup --output=backups/
n8n export:credentials --all --decrypted --output=creds.json
n8n import:credentials --input=creds.json

# Full database backup/restore
n8n export:entities --outputDir=./backup --includeExecutionHistoryDataTables=true
n8n import:entities --inputDir=./backup --truncateTables=true

# Execute a workflow by ID
n8n execute --id=<ID>

# Activate/deactivate workflows
n8n update:workflow --id=<ID> --active=true
n8n update:workflow --all --active=false

# User management
n8n user-management:reset          # Reset to setup state (forgot password)
n8n mfa:disable --email=user@x.com # Disable MFA for locked-out user

# Security audit
n8n audit

# Database migration rollback
n8n db:revert

Task D: REST API Automation

Enable the API at /api/v1/docs. Authenticate with X-N8N-API-KEY header.

# List all workflows
curl -s -H "X-N8N-API-KEY: $N8N_API_KEY" http://localhost:5678/api/v1/workflows | jq '.data[].name'

# Activate a workflow
curl -X POST -H "X-N8N-API-KEY: $N8N_API_KEY" http://localhost:5678/api/v1/workflows/<ID>/activate

# Deactivate a workflow
curl -X POST -H "X-N8N-API-KEY: $N8N_API_KEY" http://localhost:5678/api/v1/workflows/<ID>/deactivate

# Retry a failed execution
curl -X POST -H "X-N8N-API-KEY: $N8N_API_KEY" http://localhost:5678/api/v1/executions/<ID>/retry

# List failed executions
curl -s -H "X-N8N-API-KEY: $N8N_API_KEY" "http://localhost:5678/api/v1/executions?status=error" | jq '.data[] | {id, workflowId, status}'

# Create a variable
curl -X POST -H "X-N8N-API-KEY: $N8N_API_KEY" -H "Content-Type: application/json" \
  -d '{"key":"ENV","value":"production"}' http://localhost:5678/api/v1/variables

Task E: Key Environment Variables

Variable Default Purpose
DB_TYPE sqlite sqlite or postgresdb
N8N_ENCRYPTION_KEY random Credential encryption (preserve across restarts)
WEBHOOK_URL auto Public URL for webhooks behind reverse proxy
EXECUTIONS_MODE regular regular or queue (for scaling)
EXECUTIONS_DATA_PRUNE true Auto-prune old execution data
EXECUTIONS_DATA_MAX_AGE 336 Max execution age in hours
N8N_METRICS false Enable Prometheus /metrics endpoint
N8N_LOG_LEVEL info error, warn, info, debug
N8N_LOG_OUTPUT console console, file (comma-separated)
N8N_CONCURRENCY_PRODUCTION_LIMIT -1 Max concurrent production executions
N8N_RUNNERS_ENABLED false Enable task runners for Code nodes
GENERIC_TIMEZONE America/New_York Timezone for schedule triggers

Use _FILE suffix for Docker Secrets (e.g., DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/db_pass).

Examples

Example 1: Deploy n8n with PostgreSQL in production

User request: "Set up n8n in production with Docker Compose"

Actions taken:

$ mkdir n8n-production && cd n8n-production

# Generate secrets
$ openssl rand -hex 24
> a1b2c3d4e5f6...

# Create .env with POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB,
# ENCRYPTION_KEY, WEBHOOK_URL, GENERIC_TIMEZONE

# Create docker-compose.yml (Task A template)

$ docker compose up -d
> [+] Running 3/3
>  ✔ Network n8n-production_default   Created
>  ✔ Container postgres               Started
>  ✔ Container n8n                     Started

$ docker compose logs n8n --tail 5
> n8n  | Editor is now accessible via: http://localhost:5678

Example 2: Back up and restore workflows

User request: "Back up all my n8n workflows and credentials"

Actions taken:

$ docker exec -u node n8n n8n export:workflow --backup --output=/home/node/.n8n/backups/
> Successfully exported 12 workflows

$ docker exec -u node n8n n8n export:credentials --backup --output=/home/node/.n8n/backups/
> Successfully exported 8 credentials

# Copy backups out of the container
$ docker cp n8n:/home/node/.n8n/backups/ ./n8n-backups/

# Restore on a new instance
$ docker cp ./n8n-backups/ n8n-new:/home/node/.n8n/backups/
$ docker exec -u node n8n-new n8n import:workflow --separate --input=/home/node/.n8n/backups/
> Successfully imported 12 workflows

Example 3: Troubleshoot a failing instance

User request: "My n8n instance is crashing after update"

Actions taken:

$ docker compose logs n8n --tail 30
> error: Migration failed: column "x" already exists

# Rollback the last migration
$ docker exec -u node n8n n8n db:revert
> Reverted migration: AddColumnX1234567890

# Pin to previous working version in docker-compose.yml
# image: docker.n8n.io/n8nio/n8n:1.80.0

$ docker compose up -d
> n8n  | Editor is now accessible via: http://localhost:5678

Guidelines

  • Always set N8N_ENCRYPTION_KEY explicitly and back it up. Without it, credentials cannot be decrypted on a new instance.
  • Use PostgreSQL for any instance running more than a few workflows. SQLite doesn't handle concurrent writes well.
  • Set WEBHOOK_URL when behind a reverse proxy — webhooks fail silently without it.
  • Use n8n export:credentials --decrypted when migrating between instances with different encryption keys.
  • Enable execution pruning (EXECUTIONS_DATA_PRUNE=true) to prevent database bloat.
  • In queue mode, all instances must share the same N8N_ENCRYPTION_KEY and PostgreSQL database.
  • Scale workers with docker compose up -d --scale n8n-worker=N.
  • Run n8n audit periodically to detect security issues in your instance configuration.
  • For health checks, use /healthz (basic) and /healthz/readiness (DB-aware).
  • Use N8N_LOG_LEVEL=debug temporarily when troubleshooting, then revert to info.
Weekly Installs
1
GitHub Stars
15
First Seen
7 days ago
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1