Skillshub n8n-self-host

n8n Self-Host

install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/n8n-self-host" ~/.claude/skills/comeonoliver-skillshub-n8n-self-host && rm -rf "$T"
manifest: skills/TerminalSkills/skills/n8n-self-host/SKILL.md
source content

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

VariableDefaultPurpose
DB_TYPE
sqlite
sqlite
or
postgresdb
N8N_ENCRYPTION_KEY
randomCredential encryption (preserve across restarts)
WEBHOOK_URL
autoPublic 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
    .