Self-hosted n8n has a failure mode almost everyone hits eventually: everything runs fine for months, then one day the editor won't load, webhooks time out, and workflows silently stop. It's rarely a mystery when you dig in — it's almost always one of two things:
- Single-instance execution. By default, n8n executes workflows in the same process that serves the UI and receives webhooks. One heavy execution (a big loop, an AI agent chain, a large file) and everything else starves.
- The unbounded executions table. Every run writes execution data to your database. With no pruning, the table grows until Postgres becomes the bottleneck — or the disk fills.
Both have first-class fixes in n8n itself. They're just not the defaults. This post walks through the production setup n8n's own docs recommend — queue mode — plus the hardening settings that prevent the slow-motion failures.
How queue mode works
In queue mode, n8n splits into two roles:
- The main instance serves the editor UI, receives webhooks, and manages schedules — but executes nothing. When a workflow should run, it pushes a job onto a Redis queue (Bull).
- One or more workers pull jobs from the queue and execute them.
Webhook/schedule → main instance → Redis queue → worker(s) → Postgres
The practical consequences:
- A heavy execution can't freeze your editor or drop incoming webhooks — intake and execution are different processes.
- Scaling is horizontal: more load → more worker replicas. Workers are stateless (all state lives in Postgres), so replicas "just work".
- A worker crash loses nothing: queued jobs are picked up when a worker returns.
The core configuration
Both main and workers share most configuration. The critical rule: main and workers must run the same n8n version and the same encryption key, or credentials break in confusing ways.
# Shared by main + workers
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=<postgres-host>
DB_POSTGRESDB_DATABASE=<db>
DB_POSTGRESDB_USER=<user>
DB_POSTGRESDB_PASSWORD=<password>
N8N_ENCRYPTION_KEY=<same value everywhere!>
EXECUTIONS_MODE=queue
QUEUE_BULL_REDIS_HOST=<redis-host>
QUEUE_BULL_REDIS_PORT=6379
QUEUE_BULL_REDIS_PASSWORD=<redis-password>
# Main instance only
N8N_WEBHOOK_URL=https://your-domain/
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
# Workers: start with `n8n worker`; enable its health endpoint
QUEUE_HEALTH_CHECK_ACTIVE=true
The worker is the same image/binary started with n8n worker — it connects to the same Postgres and Redis and begins consuming jobs (default concurrency: 10 parallel executions per worker).
The hardening most setups skip
Execution pruning. This is the one that kills instances. Cap the history:
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=336 # keep 14 days
EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
Database backups you can actually restore. Workflows, credentials, and history all live in Postgres — it's the single point of loss. A compressed nightly pg_dump to any S3-compatible bucket costs cents; the part people skip is verifying restores. A backup you've never restored is a hope, not a backup.
Boring but worth setting: N8N_DIAGNOSTICS_ENABLED=false (telemetry off), pin your n8n image by version and digest so main and workers can never drift apart, and keep Postgres/Redis off the public internet.
Scaling checklist
When executions start queuing up:
- Add worker replicas (they're stateless — this is safe).
- Watch Postgres — execution writes are the next bottleneck; pruning keeps it manageable.
- Only then think about the main instance — it rarely needs scaling, since it executes nothing.
The one-click version
If you'd rather not assemble this by hand: we packaged this exact architecture — main + worker on a single digest-pinned image, Redis, Postgres, pruning defaults, and S3 database backups wired in — as an open-source (MIT) Railway template:
- Deploy: https://railway.com/deploy/n8n-production-stack-queue-mode
- Source: https://github.com/Kjudeh/n8n-production-stack
Full disclosure: we (Bubbles Studio) built and maintain it — versions are bumped through weekly reviewed PRs and the stack is test-deployed from scratch before every update. Everything in this post applies to it 1:1, because it is this post's architecture.
Questions about queue mode or the template — comments are open, and so are issues on the repo.
Top comments (0)