Blog
4 min read

How to Self-Host n8n: Docker, Postgres, HTTPS and Backups

A production-minded guide to self-hosting n8n: Docker Compose with Postgres instead of SQLite, the environment variables that matter (encryption key, webhook URL, timezone), HTTPS behind a reverse proxy, backups of both the database and the encryption key, updates, and when queue mode is worth it.

Self-hosting n8n gets you unlimited workflow executions for the cost of a server, and keeps your data on infrastructure you control. The quick-start docker run command works, but for anything you rely on you want Postgres, HTTPS, a fixed encryption key and backups.

n8n's options change between versions; cross-check variable names with the n8n hosting docs.

What you need

  • A Linux server with Docker and Docker Compose. 2 vCPU and 4 GB RAM is comfortable for a small team; heavy AI or data workflows want more.
  • A domain or subdomain, e.g. n8n.yourcompany.com, with an A record pointing at the server. (DNS Records Explained)

Docker Compose with Postgres

n8n defaults to SQLite, which is fine for trying it out. Postgres handles concurrent executions and larger histories better and is easier to back up properly.

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

  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
      N8N_HOST: n8n.yourcompany.com
      N8N_PROTOCOL: https
      WEBHOOK_URL: https://n8n.yourcompany.com/
      N8N_PROXY_HOPS: 1
      GENERIC_TIMEZONE: Europe/Berlin
      TZ: Europe/Berlin
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  pg_data:
  n8n_data:

Put secrets in a .env file next to it (and never commit it):

POSTGRES_PASSWORD=...
N8N_ENCRYPTION_KEY=...

Generate both with openssl rand -hex 32. (Generate a Secret Key)

Notes on the important lines:

  • 127.0.0.1:5678:5678 publishes n8n only on localhost, so it's reachable only through your reverse proxy — not directly from the internet. (Docker Compose Networking)
  • N8N_ENCRYPTION_KEY encrypts the credentials stored in n8n (API keys, OAuth tokens). Set it yourself; if n8n generates one and you lose it, every stored credential becomes unreadable.
  • WEBHOOK_URL makes webhook nodes show your public HTTPS URL instead of localhost:5678.
  • N8N_PROXY_HOPS: 1 tells n8n it is behind one reverse proxy. Set this to the actual number of trusted proxy hops, and make the last proxy forward X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto. (n8n reverse-proxy configuration)
  • depends_on with service_healthy waits for Postgres before starting n8n. (Docker Compose Healthchecks)
  • Pin a specific n8n version tag in production rather than latest, so updates happen when you choose.

Start it:

docker compose up -d
docker compose logs -f n8n

HTTPS with a reverse proxy

Caddy is the shortest route — it fetches certificates automatically:

n8n.yourcompany.com {
    reverse_proxy 127.0.0.1:5678
}

With Nginx, remember n8n's editor uses WebSockets, so pass the Upgrade and Connection headers through. (Caddy vs Nginx vs Traefik, Nginx Reverse Proxy Configuration)

Then open https://n8n.yourcompany.com and create the owner account straight away — until you do, anyone who finds the URL can.

Backups: two things, not one

  1. The database — workflows, credentials (encrypted) and execution history:
docker compose exec -T postgres pg_dump -U n8n -Fc n8n > n8n-$(date +%F).dump
  1. The encryption key — store it in your password manager. A database backup without the key restores workflows but not their credentials.

Copy backups off the server and test a restore. (How to Back Up a Postgres Database, The 3-2-1 Backup Rule)

Keep it healthy

  • Prune execution data so the database doesn't grow forever: n8n has EXECUTIONS_DATA_PRUNE and EXECUTIONS_DATA_MAX_AGE settings.
  • Update deliberately: read the release notes, back up, change the image tag, docker compose pull && docker compose up -d.
  • Lock down the server: firewall, SSH keys, automatic security updates. (Secure a New VPS)

When to use queue mode

By default one n8n process runs everything. Queue mode adds Redis and separate worker containers so executions are spread across workers — worth it when you have many concurrent or long-running workflows. Most small teams don't need it.

The licence

Self-hosting n8n for your own business is free under its Sustainable Use License. Offering n8n itself as a service to others isn't allowed without a commercial agreement, and some enterprise features require a paid licence.

The summary

  • Use Docker Compose with Postgres, a fixed N8N_ENCRYPTION_KEY and WEBHOOK_URL.
  • Bind n8n to localhost and put HTTPS in front with Caddy or Nginx (WebSockets on).
  • Back up the database and the encryption key; prune old executions.

EasySpawn gives you a managed server with HTTPS, Postgres and daily backups handled, plus Claude Code to build the app and workflows that run on it. See how it works or join the waitlist.

Related: n8n vs Zapier · Docker Compose for Local Development · Docker Volumes vs Bind Mounts · What Is Caddy?

Keep reading