Sigma Logic AI Lead with AI. Thrive with Innovation.
Automation

Running n8n in production: queue mode, workers and scaling

Why the default single-process mode blocks under load, how queue mode with Redis and workers fixes it, and the four limits you hit next.

On this page 10 sections
  1. Key takeaways
  2. Who this applies to
  3. Why the default blocks
  4. What queue mode changes
  5. A workable production shape
  6. Scaling without more workers
  7. What we set up by default
  8. When this does not apply
  9. Frequently asked questions
  10. Next step

n8n’s default runs executions in the main process, so one slow workflow blocks everything behind it and a burst of webhooks queues up. Queue mode splits execution across worker processes with Redis coordinating, and it is the difference between a deployment that survives concurrency and one that appears to work until it does not. Configure it before launch, not after the first incident.

The symptom that brings people here: workflows that ran in seconds start reporting minutes, or webhooks time out, and nothing in the workflow changed.

Key takeaways

  • Single-process mode is a development default, not a production one.
  • Queue mode needs Redis and at least one separate worker process.
  • Webhook processing and execution are separate concerns; scale them separately.
  • The next limit after workers is almost always the database, then execution-data retention.
  • Full disks are the most common self-hosted outage, and retention policy is the cause.

Who this applies to

You are self-hosting n8n and either planning for production traffic or diagnosing why it slows under load. If you are still deciding whether to self-host at all, start here.

Why the default blocks

In the default configuration, the same process that serves the editor and receives webhooks also runs workflow executions.

That is fine for one person building. Under real traffic it produces head-of-line blocking: a workflow taking four minutes occupies the process, and everything triggered during those four minutes waits. Because the delay is queueing rather than a fault, nothing errors and no alert fires - the executions simply take longer, and the graph looks like a gradual degradation rather than a structural limit.

The failure is worse for webhooks, where the far side often has a timeout of a few seconds. A blocked process means webhooks are not merely slow, they are lost, and the sending system records a delivery failure you may never see.

What queue mode changes

Queue mode separates the roles:

  • The main process serves the editor and the API, and pushes execution jobs onto a queue.
  • Redis holds the queue.
  • Worker processes pull jobs and execute them, independently of each other.
  • Webhook processes, optionally separate, receive incoming requests and enqueue them.

The immediate benefit is that a slow execution occupies one worker rather than the whole system. The second is that you can scale workers independently of everything else - two workers doubles concurrency without touching the editor or the webhook path.

Three things to know when configuring it.

Concurrency per worker matters as much as worker count. Each worker runs several executions concurrently, and the right number depends on whether your workflows are waiting on APIs or doing local work. Workflows dominated by HTTP calls tolerate higher concurrency, because most of the time is spent idle waiting.

Separate webhook processes if inbound traffic is bursty. Receiving a webhook is cheap; executing the workflow is not. Splitting them means a burst is absorbed into the queue rather than rejected.

Redis becomes a dependency you must run properly. It holds the queue, so if it is lost, queued work is lost. Persistence and monitoring on it are not optional in production.

Default mode blocks behind one slow execution; queue mode does not In the default mode a single main process runs every execution, so one slow workflow holds up everything queued behind it. In queue mode the main process only accepts and enqueues, and separate workers pull jobs through Redis, so a slow execution occupies one worker while the others continue.

Default: one process runs everything Webhooks arriving Main process one slow execution, 40s EVERYTHING ELSE WAITS 40s

Queue mode: accept, enqueue, and let workers pull Webhooks Main accepts only Redis

Worker 1Worker 2 WORKER 3, BUSY 40s

The slow execution still takes 40 seconds. It now occupies one worker instead of the whole instance, which is the entire difference.

Queue mode does not make anything faster. It stops one slow execution from being everybody's problem - and it is a configuration decision to take before launch, because the incident that reveals you needed it is the one you were trying to avoid.
## What breaks next

Adding workers moves the bottleneck rather than removing it. In roughly the order they appear:

1. Postgres connections

Every worker holds database connections. Scale to eight workers with default pool sizes and you can exhaust the connection limit on a small managed instance, which presents as errors that look nothing like a connection problem.

Set pool sizes deliberately per worker, and size the database’s connection limit against worker count times pool size, with headroom. If you are past that, put a connection pooler in front rather than raising limits indefinitely.

2. Execution data volume

The most common self-hosted outage, and it is entirely preventable.

Every execution stores its data - inputs, outputs, intermediate node results. On a busy instance that grows quickly, and the default retention is generous. The disk fills, Postgres stops accepting writes, and everything stops at once.

Set a retention policy on day one. Keep successful executions for a short window and failed ones for longer, since failures are what you investigate. Consider not saving execution data for high-volume workflows whose payloads you do not need - that setting exists per workflow and is under-used.

Alert on disk usage. This one alert prevents the single most common way these deployments fall over.

3. Memory on large payloads

A workflow processing a large file or a very large result set holds it in memory, and a worker running several such executions concurrently can exhaust its memory and be killed.

Two responses: lower concurrency on workers handling heavy workflows, or restructure the workflow to process in batches rather than loading everything at once. The second is usually correct and usually skipped.

4. Rate limits downstream

Adding workers increases the rate at which you call other people’s APIs, which is how a scaling change produces 429s in a system that was previously fine.

Concurrency limits belong at the workflow level as well as the worker level. See rate limits and API quotas across a workflow.

A workable production shape

For most business deployments, this is sufficient and less than people build:

  • Main process for the editor and API
  • Two workers to start, concurrency tuned to your workflow mix
  • Separate webhook processes if inbound traffic is bursty
  • Redis with persistence enabled and monitored
  • Managed Postgres rather than a container, with a deliberate connection limit
  • Execution retention policy set explicitly, short for successes
  • Alerts on disk, queue depth and worker health
  • Graceful shutdown configured so a deploy does not kill running executions

Queue depth is the metric that tells you when to add workers. Depth that rises during peaks and returns to zero is healthy; depth that does not return to zero means you are under-provisioned and falling behind.

Scaling without more workers

Before adding capacity, check whether the load is real.

Are workflows doing work they do not need to? A workflow triggered every minute that finds nothing to do is consuming a worker slot for nothing. Event-driven triggering usually removes far more load than another worker adds - see scheduled or event-driven.

Is one workflow responsible? Look at execution time by workflow rather than in aggregate. It is frequently one badly-shaped workflow, and fixing it is cheaper than doubling infrastructure.

Are you retrying unnecessarily? Retries consume worker capacity as well as money. See n8n error handling.

What we set up by default

Queue mode from the first deployment, two workers, explicit retention, managed Postgres, and alerts on disk and queue depth before the first workflow goes live.

The reasoning is that queue mode is difficult to add under pressure. Switching a running instance means a configuration change, a Redis dependency, a deployment change and a restart - and the moment you need it is the moment traffic is highest and everyone is already anxious. It costs an hour at the start and half a day in an incident.

The setting we most often have to argue for is execution retention, because keeping everything feels safer and the cost is invisible until the disk fills. The honest framing: unbounded retention is not a backup, it is a growing liability with no owner. Keep failures for a month, successes for a few days, and take the disk-space alert as a genuine control rather than a formality.

Where we would push back on our own default: for a genuinely low-volume internal deployment - a handful of executions a day - queue mode is more infrastructure than the problem warrants, and single-process with good monitoring is a defensible choice. The threshold is roughly whether anything is webhook-triggered from a system that will time out.

When this does not apply

n8n Cloud. The vendor handles it. This is one of the real arguments for hosted.

Low-volume internal use. A few executions a day does not need workers.

Before anything is in production. Do not build for scale you do not have; do configure queue mode before the first real traffic, because it is cheap then.

Frequently asked questions

How many workers do we need?

Start with two and watch queue depth. Add one when depth stops returning to zero during normal operation. Worker count matters less than per-worker concurrency for API-bound workflows.

Do we need separate webhook processes?

Only if inbound traffic is bursty enough that receiving requests competes with executing them. If webhooks time out under load and executions are otherwise healthy, that is the signal.

What happens to running executions during a deploy?

With graceful shutdown configured, workers finish current executions before exiting. Without it, they are killed mid-run, which for a workflow with side effects is exactly the situation replay is difficult in.

Can we run workers on separate machines?

Yes, and that is the usual way to scale past one host. They need access to Redis and Postgres, and the same environment configuration including the encryption key.

What is the single most important production setting?

Execution data retention, because ignoring it is the most common way a self-hosted instance fails outright.

Next step

If you are self-hosting without queue mode and anything is webhook-triggered, that is worth changing before traffic grows rather than after. The n8n and Make engagement covers production configuration alongside the workflows themselves.

Related: Self-hosted n8n · n8n error handling · Rate limits and API quotas across a workflow · Maintenance and support

Let's talk

Got a workflow this applies to?

Describe it in a couple of sentences. We will tell you whether it is worth automating, what we would build, and roughly what it takes.