← n8n Sizer
independent community tool

n8n Docker Compose Examples — Copy One or Generate Yours

Three complete docker-compose stacks, prerendered and copy-ready, following the official n8n-io/n8n-hosting topology (n8n:stable image, Postgres 16, Redis 7) and targeting n8n 2.x. Pick the one that matches your server, or open it in the Step 2 builder to get every number — concurrency, heap caps, pool size — tuned to your actual load. Every figure on this page comes from n8n's docs, n8n's published benchmark, or community-measured footprints, with the source named inline.

1 · Minimal: single + Postgres + Caddy (2–4 GB box)

For light-to-moderate automation on a small VPS: one n8n container on the stable image, Postgres 16 as the database (n8n 2.x dropped MySQL; Postgres is the supported server DB), and Caddy terminating TLS with automatic certificates — community measurements (~860 MB idle stack plus 40-50 MB per overlapping light run) put a comfortable single-instance floor around 2 GB. N8N_SECURE_COOKIE (the classic can't-log-in-behind-a-proxy fix) and N8N_PROXY_HOPS (correct client IPs) are already set. If you run a handful of workflows on schedules and webhooks, this is the whole stack.

# n8n 2.x · SINGLE mode · sized for 300 runs/day (peak ≈10 concurrent)
# Generated by the n8n Server Sizing Calculator · topology follows n8n-io/n8n-hosting
name: n8n
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:stable
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"   # proxy handles the public side
    env_file: .env
    volumes:
      - n8n_data:/home/node/.n8n
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }   # unrotated logs are the classic disk-killer
    depends_on:
      postgres:
        condition: service_healthy
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_DB=n8n
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=change-me
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 10s
      retries: 5
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }   # unrotated logs are the classic disk-killer
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }

volumes:
  n8n_data:
  pg_data:
  caddy_data:
  caddy_config:
# Production hardening: move task runners to a sidecar (external mode) —
#   n8n docs → Hosting → Configuration → Task runners (image: n8nio/runners:stable)

2 · Queue mode: main + workers + Redis (8 GB+)

The full queue-mode stack — main instance, Redis, workers, webhook processors — for when sustained parallel load outgrows one instance. You need it when concurrency, not speed, is the bottleneck: n8n's own benchmark took the same 4 GB hardware from 15 to 72 req/s by switching modes, but binary-heavy uploads actually got worse in queue mode and only ran clean at 32 GB — more RAM, not more workers, fixes those. Two traps are pre-handled in the file: N8N_CONCURRENCY_PRODUCTION_LIMIT overrides each worker's --concurrency in queue mode (3 workers at limit 10 = 30 concurrent), and DB_POSTGRESDB_POOL_SIZE is raised from its default of 2; OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS ships commented (the official template enables it) so manual test runs stay on the main instance where you can watch them.

# n8n 2.x · QUEUE mode · main + 2 workers + Redis + Postgres
# Generated by the n8n Server Sizing Calculator · topology follows n8n-io/n8n-hosting
name: n8n
services:
  n8n-main:
    image: docker.n8n.io/n8nio/n8n:stable
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"   # proxy handles the public side
    env_file: .env
    volumes:
      - n8n_data:/home/node/.n8n
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }   # unrotated logs are the classic disk-killer
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started

  n8n-worker:
    image: docker.n8n.io/n8nio/n8n:stable
    restart: unless-stopped
    command: worker --concurrency=10
    env_file: .env
    deploy:
      replicas: 2
    volumes:
      - n8n_data:/home/node/.n8n
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }   # unrotated logs are the classic disk-killer
    depends_on:
      - n8n-main

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --maxmemory 128mb --maxmemory-policy noeviction
    volumes:
      - redis_data:/data
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }   # unrotated logs are the classic disk-killer

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_DB=n8n
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=change-me
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 10s
      retries: 5
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }   # unrotated logs are the classic disk-killer

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }

volumes:
  n8n_data:
  redis_data:
  pg_data:
  caddy_data:
  caddy_config:
# Notes:
# · every service shares one .env → same N8N_ENCRYPTION_KEY (mandatory in queue mode)
# · binary data in 2.x queue mode lives in Postgres (filesystem unsupported; S3 = Enterprise license)
# · scale workers: docker compose up -d --scale n8n-worker=3

3 · AI workflows: same topology, the difference is .env (8 GB)

For AI-agent workflows, a single instance on an 8 GB box — the community-verified floor; 512 MB servers crash outright on AI Agent nodes. No GPU needed: model calls run on the provider's API, so RAM is the only spec that matters. This compose caps the Node heap with NODE_OPTIONS=--max-old-space-size plus N8N_RUNNERS_MAX_OLD_SPACE_SIZE at ~50-75% of RAM, so an over-hungry workflow fails predictably instead of taking the server down.

Topology matches example 1; the AI-specific part lives in .env — the heap and concurrency caps below are computed for an 8 GB box.

# ── n8n 2.x · generated by n8n Server Sizing Calculator ──
# Sized for: 300 runs/day · peak concurrency ≈10 · SINGLE mode

# ── network ──
N8N_HOST=n8n.example.com
WEBHOOK_URL=https://n8n.example.com/
N8N_PROTOCOL=https
N8N_PROXY_HOPS=1   # trust one reverse proxy in front (X-Forwarded-For)
GENERIC_TIMEZONE=UTC
N8N_ENCRYPTION_KEY=change-me-openssl-rand-hex-24

# ── execution history (drives DB size — 4,200 runs ≈ 1 GB) ──
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=336
EXECUTIONS_DATA_PRUNE_MAX_COUNT=10000
# High volume? Stop storing successful runs:
# EXECUTIONS_DATA_SAVE_ON_SUCCESS=none

# ── memory ──
# Heap: Node's default old-space can lag your RAM — pin it to ~60% of the box
NODE_OPTIONS=--max-old-space-size=4915
# 2.x: task runners are always on (N8N_RUNNERS_ENABLED is deprecated — don't set it).
# For production, run runners as a sidecar (external mode) — see docs: hosting/configuration/task-runners
N8N_RUNNERS_MAX_OLD_SPACE_SIZE=1229

# ── database ──
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=change-me
DB_POSTGRESDB_POOL_SIZE=12   # default is only 2

# ── single-mode guard against RAM storms ──
N8N_CONCURRENCY_PRODUCTION_LIMIT=10

Tuned to your load, not an average

These three files cover the common cases — for your exact case, open the Step 2 builder and generate a compose and .env tuned to your executions per day, concurrency, and RAM.

Generate my compose →

The env vars that matter in production

VariableWhat it does
EXECUTIONS_MODEregular (default) or queue. Queue requires Redis plus at least one worker container; it multiplies concurrency, not per-run speed.
N8N_CONCURRENCY_PRODUCTION_LIMITCaps overlapping production executions. Trap: in queue mode it overrides each worker's --concurrency, so 3 workers at limit 10 = 30 concurrent. Worker default 10; n8n recommends 5 or higher.
DB_POSTGRESDB_POOL_SIZEPostgres connections per instance. Default 2 — too low for queue mode; n8n docs say raise it there.
NODE_OPTIONS=--max-old-space-sizeNode.js heap cap in MB. Set to ~50-75% of container RAM so memory failures are predictable instead of random OOM kills.
N8N_RUNNERS_MAX_OLD_SPACE_SIZEThe same heap cap for task runners — enabled by default in n8n 2.x (N8N_RUNNERS_ENABLED is deprecated), so without this the main-process cap is only half the story.
EXECUTIONS_DATA_MAX_AGEExecution retention: pruned after 14 days by default (with EXECUTIONS_DATA_PRUNE=true). At ~0.25 MB per execution this is what keeps disk usage flat.
N8N_SECURE_COOKIEThe classic "can't log in behind a proxy" fix: when TLS terminates at the proxy and n8n sees plain HTTP, the auth cookie never sets. Don't blanket-disable it on a public box.
N8N_PROXY_HOPSNumber of reverse proxies in front of n8n. Must match the chain (Caddy alone = 1) or client IPs and rate limiting misbehave.
N8N_ENCRYPTION_KEYEncrypts stored credentials. Pin it in .env and back it up — lose it and every saved credential is unrecoverable.
WEBHOOK_URLPublic base URL for webhook endpoints. Without it, n8n behind a proxy hands out internal URLs that external services can't reach.

Common questions, answered with sources

Is there a ready-made n8n docker-compose example?

Yes — this page has three, prerendered from the same generator as our builder and matching the official n8n-io/n8n-hosting repo (1.7k stars): a minimal single-instance stack with Postgres and Caddy, a queue-mode stack with Redis and workers for sustained parallel load, and an AI-workflow single with heap caps for 8 GB servers. Copy them as-is.

Source: n8n-io/n8n-hosting (official repository)

Which environment variables matter for self-hosted n8n in production?

Five groups cover most incidents: concurrency (N8N_CONCURRENCY_PRODUCTION_LIMIT — per n8n docs, in queue mode it overrides each worker's --concurrency, default 10, so effective total = limit x workers), database pooling (DB_POSTGRESDB_POOL_SIZE defaults to 2 — raise it for queue mode), heap caps (NODE_OPTIONS at 50-75% of RAM), proxy settings (N8N_SECURE_COOKIE breaks login when misset; N8N_PROXY_HOPS breaks client IPs), and 14-day execution pruning.

Source: docs.n8n.io (use-environment-variables reference; control-concurrency)

Does a more powerful server make n8n faster?

Barely. In n8n's own scalability benchmark, moving single-mode from 4 GB to 32 GB improved throughput only ~8%. Switching the same 4 GB machine to queue mode went from 15 req/s to 72 req/s at 0% failures. Hardware buys concurrency headroom and binary-file capacity, not execution speed — architecture does.

Source: blog.n8n.io — the n8n scalability benchmark

How do I run 10+ workflows at once without n8n crashing?

Budget memory, then cap concurrency. Community measurements put the idle Docker stack near 860 MB, and each overlapping light execution adds roughly 40-50 MB (heavy data work: 200-500 MB). Set N8N_CONCURRENCY_PRODUCTION_LIMIT to what your RAM covers, cap the Node heap, and move to queue mode when sustained parallel load outgrows one instance.

Source: Community memory measurements; n8n scalability benchmark

What is the best VPS for n8n?

There is no best brand — sizing decides. Community measurements put the idle Docker stack near 860 MB plus 40-50 MB per overlapping light run, which makes roughly 2 GB a comfortable single-instance floor; AI-agent workflows need 8 GB (community-verified — 512 MB boxes crash on AI Agent nodes). A GPU adds nothing: LLM calls run on the provider's API.

Source: n8n community reports (memory measurements; AI Agent RAM floor)

How much disk space does n8n need?

Little, if you configure two things. Executions average ~0.25 MB on disk and n8n prunes them after 14 days by default (EXECUTIONS_DATA_MAX_AGE, per n8n docs) — 1,000 runs a day is roughly 3.5 GB in the retention window. The real disk-killer is unrotated Docker logs: set json-file max-size 10m, max-file 3 in your compose.

Source: docs.n8n.io (manage-execution-data); Docker logging documentation