Scale n8n with Queue Mode and Workers
01 Dec 2025A single n8n instance runs everything in one process: the UI, the webhook listener, and every workflow execution. One long-running workflow — a data export, a loop over a few thousand records — blocks that process and everything else waits behind it, including new webhook triggers. Queue mode splits execution out into separate worker processes so a slow workflow stops holding the rest of your automation hostage.
What queue mode actually changes
Without queue mode, n8n executes workflows in the same process that serves the editor UI and receives webhooks. With it, you run a main process that only handles the UI, API, and webhook reception, plus one or more worker processes that pull jobs off a Redis-backed queue and execute them. Webhooks still arrive instantly even if every worker is busy — the job just queues instead of blocking anything.
This requires two things you may not already have: Redis, and Postgres. SQLite can’t be shared safely across separate worker processes, so if you haven’t already moved off SQLite, do that first — queue mode won’t work without it.
flowchart LR
W["Webhook"] --> M["Main process (UI, API)"]
M -->|"enqueues job"| R["Redis queue"]
R --> W1["Worker 1"]
R --> W2["Worker 2"]
R --> W3["Worker 3"]
W1 --> P["Postgres"]
W2 --> P
W3 --> P
1. Add Redis
services:
redis:
image: redis:7-alpine
restart: unless-stopped
2. Configure the main process
services:
n8n:
environment:
EXECUTIONS_MODE: queue
QUEUE_BULL_REDIS_HOST: redis
QUEUE_BULL_REDIS_PORT: 6379
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
depends_on:
- redis
- postgres
EXECUTIONS_MODE: queue is the switch. Without it, everything else here is ignored and n8n runs in the default single-process mode regardless of what else you’ve set.
3. Add worker processes
Workers are the same n8n image, started with a different command:
services:
n8n-worker:
image: n8nio/n8n
restart: unless-stopped
command: worker
environment:
EXECUTIONS_MODE: queue
QUEUE_BULL_REDIS_HOST: redis
QUEUE_BULL_REDIS_PORT: 6379
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
depends_on:
- redis
- postgres
deploy:
replicas: 3
replicas: 3 gives you three workers pulling from the same queue. Scale this number up when you have more concurrent long-running workflows than workers to run them — check for jobs sitting in waiting state before assuming you need more, since adding workers you don’t need just adds idle processes.
4. Webhooks need a separate concern: response timing
There’s a subtlety here that trips people up. A webhook-triggered workflow, once queued, doesn’t block the HTTP response back to whatever called the webhook — n8n responds based on your webhook node’s response mode setting, not on workflow completion. If you have a webhook configured to wait for the workflow to finish and return its output, that response now has to wait on a worker to pick up and complete the job, which adds queue latency on top of execution time. For latency-sensitive webhooks, set the webhook node to respond immediately and handle the result asynchronously (write to a database, call back, whatever fits) rather than blocking on workflow completion.
Watching queue health
Check what’s queued versus running via Redis directly:
docker compose exec redis redis-cli LLEN bull:jobs:wait
A number that keeps climbing means jobs are arriving faster than your workers can drain them — that’s your signal to add replicas, not a mysterious slowdown to debug in the workflow logic.
What doesn’t change
The n8n editor UI, credentials, and workflow definitions are unaffected — queue mode is purely about where execution happens, not how you build workflows. You don’t need to modify a single existing workflow to move to queue mode; it’s an infrastructure change, not an application one. The main process still owns the UI and webhook reception, so if it goes down, new triggers stop arriving even though existing queued jobs keep processing on workers — plan your main process’s uptime accordingly, since it’s still a single point of failure for anything new coming in.