Self-Hosting n8n on Docker

automation devops docker

n8n is powerful, but relying on someone else’s cloud for your automations isn’t always ideal. Self-hosting gives you control, privacy, and no usage limits.

Here’s how to run n8n on your own infrastructure.

Quick Start with Docker

The fastest way to get started:

docker run -it --rm \
  --name n8n \
  -p 5678:5678 \
  -v ~/.n8n:/home/node/.n8n \
  n8nio/n8n

Open http://localhost:5678 and you’re running.

Production Setup with Docker Compose

For production, you’ll want persistence, environment configuration, and proper networking:

# docker-compose.yml
version: '3.8'

services:
  n8n:
    image: n8nio/n8n
    restart: always
    ports:
      - "5678:5678"
    environment:
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://${N8N_HOST}/
      - GENERIC_TIMEZONE=UTC
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:

Create a .env file:

N8N_PASSWORD=your-secure-password
N8N_HOST=n8n.yourdomain.com

Using PostgreSQL for Persistence

SQLite works for small deployments, but PostgreSQL is better for production:

version: '3.8'

services:
  postgres:
    image: postgres:13
    restart: always
    environment:
      POSTGRES_DB: n8n
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data

  n8n:
    image: n8nio/n8n
    restart: always
    ports:
      - "5678:5678"
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
    depends_on:
      - postgres
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  postgres_data:
  n8n_data:

Adding HTTPS with Traefik

Never run n8n without HTTPS in production:

version: '3.8'

services:
  traefik:
    image: traefik:v2.5
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - letsencrypt:/letsencrypt

  n8n:
    image: n8nio/n8n
    restart: always
    environment:
      - N8N_HOST=n8n.yourdomain.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      # ... other env vars
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.n8n.rule=Host(`n8n.yourdomain.com`)"
      - "traefik.http.routers.n8n.entrypoints=websecure"
      - "traefik.http.routers.n8n.tls.certresolver=letsencrypt"

volumes:
  letsencrypt:

Environment Variables Reference

Essential configuration:

# Authentication
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=secure-password

# Host configuration
N8N_HOST=n8n.yourdomain.com
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.yourdomain.com/

# Database (PostgreSQL)
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=db-password

# Timezone
GENERIC_TIMEZONE=Europe/Berlin

# Execution settings
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168  # 7 days in hours

Backups

Database Backup

docker exec postgres pg_dump -U n8n n8n > backup.sql

Volume Backup

docker run --rm \
  -v n8n_data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/n8n-backup.tar.gz /data

Automated Backup Script

#!/bin/bash
DATE=$(date +%Y%m%d)
docker exec postgres pg_dump -U n8n n8n | gzip > /backups/n8n-$DATE.sql.gz
find /backups -name "n8n-*.sql.gz" -mtime +30 -delete

Scaling for High Volume

For many concurrent workflows:

services:
  n8n:
    image: n8nio/n8n
    deploy:
      replicas: 3
    environment:
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
  
  n8n-worker:
    image: n8nio/n8n
    command: n8n worker
    deploy:
      replicas: 5
    environment:
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis

  redis:
    image: redis:6

Security Checklist

Monitoring

Add health checks:

n8n:
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:5678/healthz"]
    interval: 30s
    timeout: 10s
    retries: 3

Monitor with your existing stack (Prometheus, Grafana).

Final Thoughts

Self-hosting n8n gives you complete control over your automation infrastructure. The setup is straightforward with Docker Compose, and you can scale as your needs grow.

Start simple, add components as needed, and always prioritize backups. Your automations will thank you.


Own your infrastructure.

All posts