n8n Queue Mode Explained: Scaling with Redis and Workers

When n8n queue mode is worth it, how Redis, PostgreSQL and workers fit together, a working Docker Compose file, and fixes for the usual errors.

Published
Reading time
9 min

n8n queue mode splits n8n into a main instance that handles the editor, triggers and webhooks, and one or more workers that actually run the executions. Redis sits in between as the message queue, and PostgreSQL holds everything they share. Most self-hosted instances never need it. This post covers when to switch, how the parts fit together, a complete Compose file, and the errors you are likely to meet on the way.

Regular mode or queue mode?

By default n8n runs in regular mode: one Node.js process serves the editor, listens for webhooks, fires schedules and runs every execution. That is simpler, and for most people it is enough.

Regular mode Queue mode
Processes One n8n container Main + Redis + PostgreSQL + one or more workers
Database SQLite or PostgreSQL PostgreSQL
A heavy execution Can slow the editor and other workflows Runs on a worker, editor stays responsive
Scaling Move to a bigger server Add workers, then a bigger server
Things to maintain One container Four or more services, shared secrets

Stay on regular mode if your workflows are mostly scheduled jobs and the odd webhook and executions finish in seconds. Three extra services for a problem you do not have is just more to update and back up. If you are unsure, check docker stats during a busy hour first: a single n8n container with spare CPU and memory does not need queue mode.

Consider queue mode when:

  • Webhook bursts arrive faster than one process can finish them, and executions start queuing or timing out.
  • Long or heavy workflows (large files, big loops, AI calls that take a minute) make the editor sluggish for everyone.
  • One process is maxed out while other CPU cores sit idle. More processes is how you use them.

How the parts fit together

There are four required pieces and one optional one.

  • Main instance. Runs the editor and API, registers triggers and receives webhooks, then pushes each production execution onto the queue instead of running it.
  • Redis. The message broker. It holds the list of executions waiting to run and lets the main instance and workers talk to each other.
  • PostgreSQL. Stores workflows, credentials and execution data. Every instance reads from and writes to the same database, which is why SQLite is out.
  • Workers. n8n processes started with the worker command. Each takes jobs from Redis, runs them and writes results to PostgreSQL.
  • Webhook processors (optional). Processes started with the webhook command that only accept webhook requests. Worth it only when webhook traffic itself is the bottleneck; a load balancer then sends /webhook/* and /webhook-waiting/* to them and everything else to the main instance.

The full reference is the official queue mode documentation.

Before you change anything

The Cloud n8n VPS on our n8n hosting plans ships with n8n community edition running in Docker, with its Compose file in /opt/n8n and SQLite as the database. Queue mode is not pre-configured, so this is a change you make yourself as root. Three things to do first.

1. Take a backup. Export workflows and credentials, and make sure today's snapshot exists in the client area. Use n8n export:workflow --backup and n8n export:credentials --backup inside the container.

2. Find your encryption key. If you never set N8N_ENCRYPTION_KEY, n8n generated one on first launch and saved it in its data folder. Every instance in queue mode needs that exact value, and your existing credentials are encrypted with it.

bash
cd /opt/n8n
docker compose exec n8n cat /home/node/.n8n/config

Copy the encryptionKey value somewhere safe. Without it, stored credentials cannot be decrypted.

3. Export the data from SQLite. n8n has commands that move a whole database from SQLite to PostgreSQL:

bash
docker compose exec n8n n8n export:entities --outputDir=/home/node/.n8n/migrate

Execution history is left out by default, which is usually what you want. Add --includeExecutionHistoryDataTables=true if you need it. Both commands are described on the n8n CLI commands page.

A complete docker-compose.yml for n8n queue mode

Put the secrets in /opt/n8n/.env so the Compose file can be shared without them:

ini
N8N_ENCRYPTION_KEY=paste-the-existing-key-here
POSTGRES_PASSWORD=a-long-random-password
REDIS_PASSWORD=another-long-random-password
N8N_HOST=n8n.example.com

Then replace /opt/n8n/docker-compose.yml with this:

yaml
x-n8n-env: &n8n-env
  EXECUTIONS_MODE: queue
  N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
  DB_TYPE: postgresdb
  DB_POSTGRESDB_HOST: postgres
  DB_POSTGRESDB_PORT: 5432
  DB_POSTGRESDB_DATABASE: n8n
  DB_POSTGRESDB_USER: n8n
  DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
  QUEUE_BULL_REDIS_HOST: redis
  QUEUE_BULL_REDIS_PORT: 6379
  QUEUE_BULL_REDIS_PASSWORD: ${REDIS_PASSWORD}
  QUEUE_HEALTH_CHECK_ACTIVE: "true"
  GENERIC_TIMEZONE: Europe/London

services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_DB: n8n
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD-SHELL", "redis-cli -a $$REDIS_PASSWORD ping | grep PONG"]
      interval: 10s
      timeout: 5s
      retries: 5
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      <<: *n8n-env
      N8N_HOST: ${N8N_HOST}
      N8N_PROTOCOL: https
      N8N_WEBHOOK_URL: https://${N8N_HOST}/
      N8N_PROXY_HOPS: 1
      OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: "true"
      N8N_METRICS: "true"
      N8N_METRICS_INCLUDE_QUEUE_METRICS: "true"
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  worker:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    command: worker --concurrency=5
    environment:
      <<: *n8n-env
    depends_on:
      - n8n

volumes:
  n8n_data:
  pg_data:
  redis_data:

If your original file names the data volume differently, keep its name in both places so the main instance still sees its old data folder and config file.

A few notes on that file.

  • The x-n8n-env YAML anchor gives main and workers one shared list of settings, so none of them can miss EXECUTIONS_MODE or the encryption key.
  • The main instance is published on 127.0.0.1 only. Put a reverse proxy with HTTPS in front. Workers publish no ports at all.
  • N8N_WEBHOOK_URL is the current name. On releases older than 2.30.0 use WEBHOOK_URL; newer releases still accept the old name but log a deprecation warning.
  • Redis has a password and no published port. An open Redis port is a common way small servers get compromised.
  • Code nodes run in task runners. In the default internal mode each worker starts its own runner as a child process, so nothing extra is needed. If you switch to external runners, each worker needs its own n8nio/runners sidecar with a matching version tag, as described in the task runners documentation.

Start PostgreSQL and Redis, import the data, then bring everything up:

bash
cd /opt/n8n
docker compose up -d postgres redis
docker compose run --rm n8n import:entities --inputDir /home/node/.n8n/migrate --truncateTables true
docker compose up -d

Log in and check workflows and credentials before deleting anything old.

The environment variables that matter

All names below are from n8n's queue mode environment variables and database environment variables pages.

Variable Set on What it does
EXECUTIONS_MODE=queue Main and every worker Switches n8n from regular to queue mode
N8N_ENCRYPTION_KEY Every instance Must be identical everywhere, or credentials fail to decrypt
QUEUE_BULL_REDIS_HOST / _PORT Every instance Where Redis is (defaults localhost and 6379)
QUEUE_BULL_REDIS_PASSWORD Every instance Redis password, if you set one
DB_TYPE=postgresdb Every instance Use PostgreSQL instead of SQLite
DB_POSTGRESDB_HOST, _PORT, _DATABASE, _USER, _PASSWORD Every instance PostgreSQL connection
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true Main Runs editor test runs on workers too (default false)
QUEUE_HEALTH_CHECK_ACTIVE=true Workers Enables /healthz and /healthz/readiness

Worker concurrency is a command-line flag, not a variable: worker --concurrency=5. The default is 10. n8n recommends 5 or higher, because many workers with very low concurrency can use up database connections without getting more work done.

Scaling workers

With the file above you scale by changing a number:

bash
cd /opt/n8n
docker compose up -d --scale worker=3
docker compose ps

Parallel executions are roughly workers × concurrency, so three workers at 5 run about fifteen at once. What you can afford depends on the workflows: a worker waiting on HTTP APIs is cheap, one parsing large spreadsheets is not. Start with two workers, watch docker stats during a busy hour, and add one at a time while CPU and memory stay comfortable. On our Cloud n8n VPS L plan (6 vCores, 12 GB RAM, 100 GB NVMe) there is room for the main instance, PostgreSQL, Redis and several workers. If the server itself becomes the limit, we resize it to the next plan on request with the disk and IP kept and a short reboot; the plans are listed on the n8n hosting page, and the same sizes are available as a plain Cloud VPS.

One catch: queue mode does not support n8n's filesystem binary data mode. If workflows pass large files between nodes, read the binary data section of the queue mode docs and consider S3-compatible storage.

Monitoring

Logs. The quickest check. A worker that cannot reach Redis or PostgreSQL says so within seconds and restarts in a loop.

bash
docker compose logs -f --tail=50 worker
docker compose logs -f --tail=50 n8n

Health endpoints. With QUEUE_HEALTH_CHECK_ACTIVE=true, each worker answers on port 5678 inside the Docker network. /healthz says the process is up, /healthz/readiness says its database and Redis connections are ready:

bash
docker run --rm --network n8n_default curlimages/curl -s http://n8n-worker-1:5678/healthz/readiness

docker compose ps shows the exact container names.

Metrics. N8N_METRICS=true turns on a Prometheus /metrics endpoint on the main instance, and N8N_METRICS_INCLUDE_QUEUE_METRICS=true adds job counts for the queue. Check it from the server itself:

bash
curl -s http://127.0.0.1:5678/metrics | grep -i queue

Block /metrics in your reverse proxy.

Redis. Two commands tell you most of what you need:

bash
docker compose exec redis sh -c 'redis-cli -a "$REDIS_PASSWORD" ping'
docker compose exec redis sh -c 'redis-cli -a "$REDIS_PASSWORD" info | grep -E "used_memory_human|connected_clients"'

connected_clients should grow as you add workers. Memory that only climbs usually means jobs arrive faster than workers finish them.

Common errors and fixes

Workers start but nothing runs. Usually EXECUTIONS_MODE=queue is missing on the main instance, so it keeps running executions itself, or the worker points at a different Redis. Check both with docker compose exec worker env | grep -E 'EXECUTIONS_MODE|QUEUE_BULL'.

Credentials fail to decrypt on workers. The encryption key differs. Set the same N8N_ENCRYPTION_KEY everywhere, copied from the main instance's config file, not newly generated.

Redis connection refused or timeouts. The host must be the Compose service name (redis), not localhost, and every instance needs QUEUE_BULL_REDIS_PASSWORD if Redis has one. n8n exits after QUEUE_BULL_REDIS_TIMEOUT_THRESHOLD (default 10,000 ms) without Redis, so this shows up as restart loops.

Too many database connections. Each instance opens its own pool (DB_POSTGRESDB_POOL_SIZE, default 2). Prefer fewer workers at concurrency 5 or more, and raise PostgreSQL's max_connections if needed.

Webhooks return 404 after the switch. Check the workflow is still active after the import and that N8N_WEBHOOK_URL matches the public address.

Test runs behave differently from production. Without OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true, test runs happen on the main instance, which may differ from the workers.

Is self-hosting still the right call?

Queue mode is where self-hosting asks the most of you: four services, shared secrets, your own monitoring. In return you get unlimited workflows and executions on hardware you size. n8n Cloud handles scaling for you and is the better fit if nobody wants to look after Redis and PostgreSQL. We compare the two honestly in self-hosted n8n vs n8n Cloud.

If you run it yourself, a Cloud n8n VPS from our n8n hosting range gives you the starting point in /opt/n8n, root access, a daily snapshot and a 30-day money-back guarantee.

Frequently asked questions

Is queue mode pre-configured on the Cloud n8n VPS?

No. The server ships with n8n in Docker in /opt/n8n on SQLite. You add Redis, PostgreSQL and workers yourself with root access; there is no managed queue-mode setup.

Which plan should I pick for n8n queue mode?

The L plan (6 vCores, 12 GB RAM, 100 GB NVMe, $22.99/mo) is the one we position for queue mode with worker processes. If you outgrow it, open a ticket and we move you to the next size with disk and IP kept. See n8n hosting.

Can I run queue mode on SQLite?

n8n does not recommend it. Every instance has to reach the same database, so use PostgreSQL with DB_TYPE=postgresdb.

Do workers need the same encryption key as the main instance?

Yes. Set the same N8N_ENCRYPTION_KEY on the main instance, every worker and any webhook processor, or workers cannot decrypt credentials.

Does queue mode need an n8n Enterprise licence?

Queue mode with one main instance and workers runs on the community edition. Multi-main setups (several main instances) are an Enterprise feature that needs an n8n licence.

Will you set up queue mode for me?

The Cloud n8n VPS is self-managed, so the setup is yours. Workflow building and custom work can be done by our developers at $35/hour, quoted first.

#automation#n8n#docker#redis#postgresql

Keep reading

More from n8n & Automation

All guides

n8n & Automation

n8n Cloud vs Self-Hosted: Costs, Limits and When to Switch

n8n Cloud bills by executions, self-hosted n8n bills by server and your time. Limits, GDPR, upkeep, licence and a checklist for when to switch.

8 min read →

WHMCS

WHMCS + DirectAdmin: Automate Account Setup, Billing and Suspensions

Connect WHMCS to DirectAdmin with a login key, map products to packages, set the cron and automation rules, then test the whole billing cycle safely.

6 min read →

Linux

How to Schedule Tasks With Cron (and Know When They Fail)

Cron syntax with examples, where jobs live, running them as the right user, capturing output, and how to get told when a job fails instead of finding out later.

3 min read →