n8n Self-Hosted on Docker

By LK Wood IV · 2026-06-13 · ~11 min read · St. Louis County, MO

n8n is a workflow automation tool with 400+ integrations — the self-hosted alternative to Zapier, Make, and IFTTT. The hosted version charges per workflow execution. Self-hosted runs on your own machine with no execution limits, full access to internal services, and no data leaving your network.

This guide sets up n8n with PostgreSQL, persistent storage, HTTPS via Nginx Proxy Manager, and automated backups.

What you’ll have at the end

  • n8n running in Docker with PostgreSQL as the database
  • Accessible at n8n.yourdomain.com with valid HTTPS
  • Automatic backups of the n8n database and workflows
  • Ready to connect to internal homelab services (Proxmox API, Grafana, Home Assistant)

Prerequisites

  • Docker running on your homelab host
  • Nginx Proxy Manager already set up with a wildcard SSL cert (or you’ll use direct IP access)
  • A proxy Docker network (created by the NPM guide): docker network create proxy

Step 1: Docker Compose setup

mkdir -p /opt/stacks/n8n && cd /opt/stacks/n8n

Create the compose file with n8n and PostgreSQL:

# /opt/stacks/n8n/docker-compose.yml
services:
  postgres:
    image: postgres:16-alpine
    container_name: n8n-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: n8n
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - ./postgres:/var/lib/postgresql/data
    networks:
      - n8n-internal
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 5s
      timeout: 5s
      retries: 5

  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      # Database
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_PORT: 5432
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: ${DB_PASSWORD}
      # Server config
      N8N_HOST: n8n.yourdomain.com
      N8N_PORT: 5678
      N8N_PROTOCOL: https
      WEBHOOK_URL: https://n8n.yourdomain.com/
      # Security
      N8N_BASIC_AUTH_ACTIVE: "true"
      N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER}
      N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD}
      # Timezone
      GENERIC_TIMEZONE: America/Chicago
      TZ: America/Chicago
    volumes:
      - ./n8n:/home/node/.n8n
    networks:
      - n8n-internal
      - proxy

networks:
  n8n-internal:
    driver: bridge
  proxy:
    external: true

Create the environment file:

cat > .env << 'EOF'
DB_PASSWORD=generate-a-strong-password-here
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=another-strong-password-here
EOF

chmod 600 .env

Create the data directories:

mkdir -p ./postgres ./n8n

Start the stack:

docker compose up -d

# Watch logs to confirm startup
docker compose logs -f n8n

On first startup, n8n runs database migrations which takes 30–60 seconds. You’ll see “Workflow manager is now running” when it’s ready.

Access n8n at http://your-host-ip:5678 or configure NPM to serve it at https://n8n.yourdomain.com.

Pin the n8n version — before going to production, pin to a specific tag:

    image: n8nio/n8n:1.87.0    # check latest at hub.docker.com/r/n8nio/n8n/tags

This prevents surprise breaking changes from :latest upgrades.

Step 2: Configure Nginx Proxy Manager

In NPM → Add Proxy Host:

  • Domain: n8n.yourdomain.com
  • Scheme: http
  • Forward hostname: n8n (container name, on the proxy network)
  • Port: 5678
  • Websockets Support: ON (n8n uses websockets for the editor)
  • SSL: wildcard cert, Force SSL on

Visit https://n8n.yourdomain.com — you should see the n8n login with basic auth.

Step 3: First login and owner account

After the basic auth challenge, n8n shows an account setup page. Create the owner account — this is separate from the basic auth and is your n8n admin account. Use a strong password distinct from the basic auth.

Disable n8n’s built-in basic auth once you’re behind NPM with SSL and access lists — or keep it as a second layer of auth. Your choice.

Step 4: Connect your first integration

n8n’s integration list is at the bottom-left “Credentials” section. To connect a service:

  1. Click “Add Credential” → search for your service (Gmail, Slack, GitHub, Airtable, etc.)
  2. Follow the OAuth flow or paste the API key
  3. Credentials are stored encrypted in the PostgreSQL database

Connecting to homelab services:

n8n can reach any LAN IP directly. In an HTTP Request node:

  • URL: http://192.168.1.2:8006/api2/json/nodes (Proxmox API)
  • Authentication: Header Auth with Authorization: PVEAPIToken=user@realm!name=token-value

This works because n8n is running inside your LAN — the request goes directly to Proxmox without any internet hop.

Step 5: Example workflows

Workflow 1: Notify when a Proxmox VM stops (via Grafana alert webhook)

  1. Trigger: Webhook (n8n generates a URL you paste into Grafana Alertmanager)
  2. Node: IF → check if alert status is “firing”
  3. Node: Send notification to Discord/Telegram/email

Workflow 2: Daily Homelab health summary

  1. Trigger: Schedule → daily at 8am
  2. HTTP Request: http://uptime-kuma:3001/api/status-page/... → get service status
  3. HTTP Request: Proxmox API → get VM/LXC status
  4. Function: format the data into a summary
  5. Notification: send to Discord/Telegram

Workflow 3: Auto-backup trigger after Immich import

  1. Trigger: Webhook (call from an immich-go post-import script)
  2. Execute Command node: run your restic backup script
  3. Notification: send completion status to Slack

Step 6: Queue mode (optional, for reliability)

Queue mode offloads workflow execution to separate worker processes via Redis. This prevents the main n8n process from being blocked by long-running workflows and recovers from crashes without losing in-progress jobs.

Add Redis to the compose file:

  redis:
    image: redis:7-alpine
    container_name: n8n-redis
    restart: unless-stopped
    networks:
      - n8n-internal
    volumes:
      - ./redis:/data

Update n8n’s environment in the compose file:

      EXECUTIONS_MODE: queue
      QUEUE_BULL_REDIS_HOST: redis
      QUEUE_BULL_REDIS_PORT: 6379

Add a worker service:

  n8n-worker:
    image: n8nio/n8n:latest   # same version as n8n
    container_name: n8n-worker
    restart: unless-stopped
    command: worker
    depends_on:
      - n8n
      - redis
      - postgres
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_PORT: 5432
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: ${DB_PASSWORD}
      QUEUE_BULL_REDIS_HOST: redis
      QUEUE_BULL_REDIS_PORT: 6379
    volumes:
      - ./n8n:/home/node/.n8n
    networks:
      - n8n-internal

Redeploy:

docker compose up -d

Step 7: Backups

n8n data lives in two places:

  1. PostgreSQL database — workflows, credentials, execution history
  2. ./n8n volume — local files, SSH keys, custom nodes

Database backup:

# Manual backup
docker exec n8n-postgres pg_dump -U n8n n8n > /mnt/backups/n8n/n8n-$(date +%Y%m%d).sql

# Automated via cron
echo "0 4 * * * root docker exec n8n-postgres pg_dump -U n8n n8n > /mnt/backups/n8n/n8n-\$(date +\%Y\%m\%d).sql" >> /etc/crontab

Credentials are encrypted in the database using a key derived from n8n’s N8N_ENCRYPTION_KEY environment variable (set automatically on first run, stored in ./n8n/config). Back up ./n8n/config alongside the database — without the encryption key, the credentials in a backup can’t be decrypted.

If you add N8N_ENCRYPTION_KEY to your .env file explicitly (instead of letting n8n generate it), you own the key and can restore credentials even if the ./n8n volume is lost:

N8N_ENCRYPTION_KEY=your-32-char-random-string-here

Resource usage

On a Debian 12 LXC with PostgreSQL, n8n idle, 15 active workflows:

ServiceRAM
n8n~180 MB
PostgreSQL~60 MB
Redis (if using queue mode)~15 MB
Total~255 MB

n8n is not resource-heavy at idle. CPU usage spikes during workflow execution proportional to the complexity of the workflow, not the number of workflows defined.


n8n pairs well with Uptime Kuma for monitoring-triggered workflows — the Docker Compose starter stack has both running on the same proxy network. For the broader self-hosted app ecosystem n8n fits into, see the 12 best self-hosted apps guide.