Contents

Self-Hosting n8n Tutorial: Docker Compose, PostgreSQL, and HTTPS

After testing successfully on your own computer, you need to deploy n8n to a server with a fixed URL so your workflows can run 24/7 and external services such as Webhook and Telegram can connect to them.

What you need

  • A Linux cloud server (VPS). For light use, 1–2 CPU cores and 2 GB of memory are enough; you'll need more for many workflows or to run local AI models.
  • A domain, with the DNS A record for a subdomain (such as n8n.example.com) pointing to the server's IP address.
  • Docker and Docker Compose installed on the server (see Docker tutorial: installation).
  • Ports 80 and 443 open in the firewall.

Architecture

Internet ──HTTPS──▶ Caddy (automatic certificates) ──▶ n8n:5678 ──▶ PostgreSQL
  • Caddy: Reverse proxy that automatically obtains and renews Let's Encrypt certificates
  • PostgreSQL: The recommended database for production (the default SQLite database is suitable for personal use or testing)
  • Neither n8n nor the database is exposed directly to the internet

Starting with n8n 2.x, MySQL / MariaDB are no longer supported as databases for n8n. Use PostgreSQL or SQLite.

Files

Create the /opt/n8n directory on the server:

/opt/n8n/
├── compose.yaml
├── .env
└── Caddyfile

.env

DOMAIN=n8n.example.com
POSTGRES_PASSWORD=Replace with a long, random password
N8N_ENCRYPTION_KEY=Replace with a different long, random string

Generate a random string:

openssl rand -hex 32

N8N_ENCRYPTION_KEY encrypts all credentials. Be sure to back it up separately and securely; if you lose it, none of your credentials will be usable.

Caddyfile

{$DOMAIN} {
    reverse_proxy n8n:5678
}

compose.yaml

services:
  caddy:
    image: caddy:2
    ports:
      - "80:80"
      - "443:443"
    environment:
      DOMAIN: ${DOMAIN}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
    restart: unless-stopped

  postgres:
    image: postgres:17
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 5s
      retries: 10
    restart: unless-stopped

  n8n:
    image: n8nio/n8n:2.42.7   # Change this to the fixed version you want to use
    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_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
      N8N_HOST: ${DOMAIN}
      N8N_PORT: 5678
      N8N_PROTOCOL: https
      N8N_WEBHOOK_URL: https://${DOMAIN}/
      N8N_PROXY_HOPS: 1
      NODE_ENV: production
      GENERIC_TIMEZONE: Asia/Taipei
      TZ: Asia/Taipei
      N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true"
      EXECUTIONS_DATA_PRUNE: "true"
      EXECUTIONS_DATA_MAX_AGE: 336
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped

volumes:
  caddy_data:
  pg_data:
  n8n_data:

Important environment variables

VariableDescription
N8N_HOST, N8N_PROTOCOLThe public domain and protocol; these affect OAuth callback URLs and other settings.
N8N_WEBHOOK_URLThe public URL for webhooks. If you don't set it, Webhook nodes will show http://localhost:5678/.... For versions earlier than n8n 2.30, use WEBHOOK_URL instead.
N8N_PROXY_HOPSSet this to 1 when n8n is behind a reverse proxy so it can get the user's real IP address.
N8N_ENCRYPTION_KEYThe key used to encrypt credentials
GENERIC_TIMEZONEThe time zone used for scheduled workflows
EXECUTIONS_DATA_PRUNE, EXECUTIONS_DATA_MAX_AGEAutomatically delete execution records older than the specified number of hours (336 hours = 14 days) to prevent the database from growing indefinitely.

Start n8n

cd /opt/n8n
docker compose up -d
docker compose logs -f n8n

Within a few minutes, Caddy will obtain a certificate automatically. Open https://n8n.example.com and create an owner account.

Create the account right away. Until you set up the owner account, anyone who visits this URL can create an account first.

Security recommendations

  • Use a fixed image tag; don't use latest. Read the release notes before upgrading.
  • Open only ports 22, 80, and 443 on the server, and allow SSH key-based login only.
  • Enable two-step verification for your account (in personal settings).
  • Dangerous nodes you don't need (such as nodes that run system commands) are disabled by default in 2.x. Don't enable them casually.
  • Back up regularly (see Backup and updates).

Connect a local AI model

To let n8n use a local Ollama model, add an ollama service to the same compose.yaml file. See Connect n8n to Ollama. AI models need more memory and computing resources, so a typical small VPS can't run them.

High-volume executions: queue mode

When you have many workflows that run frequently, you can enable queue mode: the main process receives triggers and hands them to multiple workers for parallel execution through a Redis queue. This is an advanced deployment setup; see the queue mode section in the official documentation.

Updated October 12, 2026