DEV Community

Kjudeh for Bubbles Studio

Posted on

Running n8n in production: queue mode, workers, and the settings that keep it alive

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:

  1. 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.
  2. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. Add worker replicas (they're stateless — this is safe).
  2. Watch Postgres — execution writes are the next bottleneck; pruning keeps it manageable.
  3. 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:

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)