Short answer
To run n8n reliably on Kubernetes, use queue mode: one main pod for the editor, API and triggers, a scalable pool of worker pods that execute workflows, optional webhook-processor pods for inbound traffic, and external PostgreSQL and Redis. Every pod must share the same N8N_ENCRYPTION_KEY. Workers and webhook processors scale horizontally on the free edition. Running more than one main pod (multi-main) needs an Enterprise licence.
A single n8n container works until one heavy workflow makes the editor unresponsive, or a pod restart drops in-flight executions. Queue mode fixes that by separating the process that decides what to run from the processes that run it. This guide covers the Kubernetes side: which components you need, the settings that must match across pods, how to route webhooks, how to scale workers, and what “high availability” can and cannot mean for n8n.
Designing the workflows themselves (idempotency, retries, reconciliation) is a separate topic, covered in Designing Resilient Multi-Platform Data Pipelines with n8n and Webhooks. Here the focus is the platform those workflows run on. Details reflect the n8n 2.x documentation and the official Helm chart as of October 2026.
What queue mode changes
In the default mode, one process serves the UI, receives webhooks and executes every workflow. In queue mode, production executions are handed to workers through Redis, and PostgreSQL stores workflows, credentials and execution data.
| Component | Started with | Responsibility | Replicas |
|---|---|---|---|
| Main | n8n (default command) |
Editor UI, REST API, schedules and polling triggers, enqueuing executions | 1, or several with multi-main (Enterprise) |
| Worker | n8n worker |
Pulls jobs from Redis and runs workflow executions | Scale horizontally |
| Webhook processor | n8n webhook |
Receives production webhook calls and enqueues them | Scale horizontally |
| Redis | External service | Message broker between main, webhook processors and workers | Managed or HA deployment |
| PostgreSQL | External service | Workflows, credentials, execution history | Managed or HA deployment |
Hard requirements
PostgreSQL, not SQLite
The queue mode docs say running a distributed setup over SQLite isn’t supported. n8n’s database page lists the supported PostgreSQL majors as the latest two actively maintained versions plus one older one for compatibility. As of July 2026 that was 17 and 18, plus 16. Aurora PostgreSQL is marked experimental, and other PostgreSQL-compatible products such as AlloyDB and CockroachDB are not supported. Check the page before choosing a version, because the range moves every year.
One encryption key, everywhere
n8n encrypts stored credentials with N8N_ENCRYPTION_KEY. The main instance’s key must be shared with every worker and webhook processor. A worker with a different or auto-generated key cannot decrypt credentials, so every execution that touches them fails. Create the key once, store it in a Kubernetes Secret (or a file mounted from your secret manager), and back it up separately from the database. A database backup without the key cannot be restored into a working instance.
Binary data that every pod can read
n8n does not support filesystem binary data mode with queue mode, because a file written by one worker is not on another pod’s disk. The binary data docs say to use N8N_DEFAULT_BINARY_DATA_MODE=database. S3 external storage is the other option, but it is a paid feature: it needs a Business or Enterprise licence, and n8n will not start in s3 mode without one.
Task runners as sidecars
Code node execution runs in task runners. The task runner docs recommend external mode in production: an n8nio/runners sidecar next to each worker, with the same version as the n8n image and a shared N8N_RUNNERS_AUTH_TOKEN. Internal mode runs the runner as a child process of n8n, and the docs say it is deprecated from n8n 3.0.
The environment variables that matter
| Variable | Set on | Purpose |
|---|---|---|
EXECUTIONS_MODE=queue |
All n8n pods | Enables queue mode |
N8N_ENCRYPTION_KEY |
All n8n pods | Must be identical everywhere |
DB_TYPE=postgresdb, DB_POSTGRESDB_* |
All n8n pods | Shared database |
QUEUE_BULL_REDIS_HOST, _PORT, _PASSWORD, _DB |
All n8n pods | Redis connection (defaults: localhost, 6379, db 0) |
QUEUE_BULL_REDIS_TLS, QUEUE_BULL_REDIS_CLUSTER_NODES |
All n8n pods | TLS and Redis Cluster seed nodes |
QUEUE_HEALTH_CHECK_ACTIVE=true |
Workers | Exposes /healthz and /healthz/readiness (DB and Redis readiness) |
N8N_DISABLE_PRODUCTION_MAIN_PROCESS=true |
Main | Stops main handling production webhooks once webhook processors exist |
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true |
Main | Runs manual editor executions on workers too (default false) |
N8N_GRACEFUL_SHUTDOWN_TIMEOUT |
Workers | How long a stopping worker waits for running executions (replaces the deprecated QUEUE_WORKER_TIMEOUT) |
The full list is on the queue mode environment variables page. It includes the worker lock and stalled-job intervals, which you rarely need to change.
The official Helm chart
n8n now maintains an official chart in the n8n-io/n8n-hosting repository, published to GitHub Container Registry. It requires Helm 3.12+ and Kubernetes 1.25+. Queue mode is the chart default, and the chart deliberately does not bundle PostgreSQL or Redis, so you point it at managed or separately deployed instances. Chart 1.x installs n8n 2.x. Older community charts (8gears and others) still exist, but use the official one for new deployments.
# values.yaml: keys from the official chart 1.x; check your chart version's values.yaml
queueMode:
enabled: true
workerReplicaCount: 2
workerConcurrency: 10
webhookProcessor:
enabled: true
replicaCount: 2
disableProductionWebhooksOnMainProcess: true
database:
type: postgresdb
useExternal: true
host: n8n-postgres.internal
port: 5432
database: n8n
user: n8n
passwordSecret: { name: n8n-db-password, key: password }
redis:
enabled: true
useExternal: true
host: n8n-redis.internal
port: 6379
secretRefs:
existingSecret: n8n-core-secrets # holds N8N_ENCRYPTION_KEY
keda:
enabled: true # requires KEDA installed in the cluster
worker: { minReplicaCount: 2, maxReplicaCount: 20 }
helm install n8n oci://ghcr.io/n8n-io/n8n-helm-chart/n8n \
--version <pinned-chart-version> -n n8n --create-namespace -f values.yaml
Pin the chart version and upgrade on purpose. Release 1.16.0 (14 October 2026), for example, made the chart refuse to render more than one main replica unless multi-main is enabled. That is a useful guard, but it will fail an unpinned upgrade of a cluster that was quietly running two main pods.
Routing webhooks
With webhook processors enabled, the load balancer has to split traffic by path. The n8n docs specify:
/webhook/*(production webhook triggers) and/webhook-waiting/*(“send and wait” resumes) go to the webhook processor Service./webhook-test/*(test URLs from the editor) and everything else go to main.
The docs also advise against adding main to the webhook pool, so that a burst of inbound calls cannot slow the editor. The chart renders a separate Ingress for webhook processors when both ingress.enabled and ingress.webhookProcessor.enabled are set. If you manage Ingress yourself, the rule looks like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: n8n
spec:
ingressClassName: nginx
rules:
- host: n8n.example.com
http:
paths:
- path: /webhook/
pathType: Prefix
backend: { service: { name: n8n-webhook, port: { number: 5678 } } }
- path: /webhook-waiting/
pathType: Prefix
backend: { service: { name: n8n-webhook, port: { number: 5678 } } }
- path: /
pathType: Prefix
backend: { service: { name: n8n-main, port: { number: 5678 } } }
Set the public base URL (WEBHOOK_URL, or webhook.url in the chart) to the external hostname. Otherwise n8n shows internal Service URLs in webhook nodes. A misrouted path usually shows up as a 404 or “Cannot POST” on production webhooks while test webhooks still work.
What high availability actually means here
| Failure | Without multi-main | With multi-main (Enterprise) |
|---|---|---|
| A worker pod dies | Other workers carry on; its active jobs are detected as stalled and picked up again | Same |
| A webhook processor dies | Remaining replicas take the traffic | Same |
| The main pod dies | Editor, API, schedules and polling triggers are down until Kubernetes restarts it | A follower takes over leadership; the editor stays up behind sticky sessions |
| Redis or PostgreSQL is lost | Everything stops | Everything stops |
Multi-main (N8N_MULTI_MAIN_SETUP_ENABLED=true) is documented as a self-hosted Enterprise feature. It needs Postgres, Redis, identical n8n versions on every main, and sticky sessions. One main is the leader and runs the at-most-once work, such as schedules. The leader key has a 10-second TTL by default. Do not try to get there by setting two main replicas without it: each main runs its own triggers, so scheduled workflows fire once per pod.
For most teams, “HA” on the free edition means redundant workers and webhook processors, managed HA Redis and Postgres, a PodDisruptionBudget, and a main pod that restarts quickly. Be honest with stakeholders that schedules pause during a main restart.
Scaling workers
Each worker runs up to --concurrency executions at once. The default is 10, and n8n recommends 5 or more. Scale on queue depth, not CPU: a worker waiting on slow HTTP calls uses little CPU while the queue grows. The chart’s KEDA integration scales workers on the length of the Redis waiting list (the chart default watches bull:jobs:wait, so change it if you set QUEUE_BULL_PREFIX).
Before you add workers, check what they call. Twenty workers with concurrency 10 can make 200 simultaneous calls to an API that allows 100 requests per 10 seconds. The downstream rate limit, not your cluster, sets the real ceiling. If you need a strict per-destination limiter, a dedicated queue in front of that API does better than n8n’s retry settings. Scaling webhook ingestion with Redis and BullMQ covers that pattern.
Operational details that bite
- Redis eviction. n8n’s queue keys live in Redis, so a Redis that evicts keys under memory pressure can drop queued executions. Run it with
maxmemory-policy noeviction, as BullMQ’s production guidance recommends for Bull-family queues, and do not share it with a cache. - Graceful shutdown. Set the pod’s
terminationGracePeriodSecondslonger thanN8N_GRACEFUL_SHUTDOWN_TIMEOUT, so Kubernetes does not kill a worker that is still finishing executions. - Readiness probes. Point worker readiness at
/healthz/readiness, which reports database and Redis connectivity. - Version skew. Upgrade main, workers, webhook processors and runner sidecars together. The runners image must match the n8n version.
- Execution data growth. Configure execution pruning. Execution history in Postgres grows quickly when workers run high volumes.
Checklist
- External PostgreSQL on a supported major, and HA Redis with
noeviction. N8N_ENCRYPTION_KEYin a Secret, identical on every pod, backed up separately.EXECUTIONS_MODE=queueeverywhere, and binary data indatabasemode (or licensed S3).- Webhook processors behind
/webhook/and/webhook-waiting/, and main excluded from that pool. - External task runner sidecars with matching versions.
- Workers autoscaled on queue depth, capped by downstream rate limits.
- Exactly one main replica unless you hold an Enterprise licence and run multi-main.
- Chart version pinned, and upgrades rehearsed in staging.
If one of the systems on the other end is a CRM with tight API limits, see GoHighLevel CRM sync pipelines with webhooks and workers for budgeting calls per account.
Questions people actually ask
- Is n8n queue mode free, or does it need a licence?
- Queue mode with workers and webhook processors is documented without a licence requirement. Multi-main setup, viewing running workers in the UI, and S3 external storage are the licensed features to watch for.
- Can I run two n8n main pods for redundancy?
- Only with multi-main enabled, which needs an Enterprise licence. Without it, each main pod runs its own schedules and triggers, so workflows fire twice. The official chart now refuses to render that configuration.
- Is there an official n8n Helm chart?
- Yes. It is maintained in the n8n-io/n8n-hosting repository and installed from
oci://ghcr.io/n8n-io/n8n-helm-chart/n8n. It supports queue mode, webhook processors, HPA and KEDA, and you provide PostgreSQL and Redis yourself.
Moving n8n from a single container to Kubernetes?
I help teams plan and review n8n queue-mode deployments, from encryption key handling and webhook routing to worker scaling against real API limits.