
Self-Hosting n8n on Kubernetes: HA, Queue Mode, Autoscaling, and Backups That Actually Restore
The complete production playbook for n8n on Kubernetes — queue mode topology, KEDA autoscaling on Redis depth, Postgres HA and PITR, encryption-key rotation, zero-downtime upgrades, and the backup drill you must actually run.
For any team past ~10k executions/day, self-hosted n8n in queue mode on Kubernetes — with Postgres, Redis, S3 for binary data, KEDA-driven worker autoscaling, and a rehearsed restore drill — is the operational sweet spot. Split main/webhook/worker into separate deployments, autoscale workers on Redis queue depth (not CPU), keep the encryption key in a KMS you can rotate, and run a monthly game day where you actually restore from backup. You don't have a backup until you've restored one.
- Queue mode is not optional above trivial volume.
- Split main, webhook, and worker into separate deployments.
- Autoscale workers on Redis queue depth via KEDA, not CPU via HPA.
- Store binary data in S3-compatible storage — never in Postgres.
- Rotate the n8n encryption key with a documented runbook; losing it bricks every stored credential.
- Restore from backup monthly — scheduled, on the calendar, with a smoke workflow.
- Upgrade workers first, main last; drain the queue before rolling.
- Keep Postgres and Redis close (same VPC / same region) — network latency dominates n8n throughput.
When self-hosting is the right call
Managed n8n Cloud is a great default. Move to self-hosted Kubernetes when at least two of these are true:
- You process regulated or highly sensitive data that cannot leave your VPC.
- You exceed ~10k executions/day and the Cloud plan starts biting.
- You need custom nodes, custom Docker images, or unusual runtime dependencies.
- Your platform team already operates Kubernetes competently.
The topology — three deployments, three responsibilities
| Deployment | Role | Replicas | Scaling signal |
|---|---|---|---|
| main | UI, editor, cron scheduler, leader-elected | 1–2 (leader lock) | Static |
| webhook | Inbound HTTP for webhooks — stateless | 2–N | Requests / sec |
| worker | Executes workflow steps pulled from Redis | N (auto) | Redis queue depth |
Backing services:
- Postgres 15+ — execution metadata, credentials, workflows. HA via managed (Cloud SQL / RDS / Aiven) or a Postgres operator (CloudNativePG).
- Redis 7+ — job queue (BullMQ). HA via managed or Redis Operator.
- Object storage — S3, MinIO, or GCS for binary data (PDFs, images, videos).
- External Secrets — pull credentials from Vault / AWS Secrets Manager / GCP Secret Manager.
A minimal-but-real Helm values.yaml
# values.yaml — production-shaped n8n
image:
repository: n8nio/n8n
tag: 1.72.0 # pin, never :latest
env:
EXECUTIONS_MODE: queue
QUEUE_BULL_REDIS_HOST: redis
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: pg-primary
DB_POSTGRESDB_DATABASE: n8n
N8N_ENCRYPTION_KEY: <from-external-secret>
N8N_DEFAULT_BINARY_DATA_MODE: s3
N8N_EXTERNAL_STORAGE_S3_HOST: s3.amazonaws.com
N8N_EXTERNAL_STORAGE_S3_BUCKET_NAME: n8n-binary-prod
N8N_METRICS: 'true'
N8N_LOG_LEVEL: info
N8N_PAYLOAD_SIZE_MAX: '16'
N8N_RUNNERS_ENABLED: 'true'
main:
replicas: 2
resources: { requests: { cpu: 500m, memory: 1Gi }, limits: { memory: 2Gi } }
webhook:
replicas: 3
resources: { requests: { cpu: 250m, memory: 512Mi } }
worker:
minReplicas: 3
maxReplicas: 40
resources: { requests: { cpu: 500m, memory: 1Gi }, limits: { memory: 2Gi } }Autoscaling that responds to actual load — KEDA + Redis
CPU-based HPA lags real load by 30–60 seconds because workers spend most of their time waiting on I/O, not burning CPU. Scale on queue depth instead:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: n8n-worker
spec:
scaleTargetRef:
name: n8n-worker
pollingInterval: 15
cooldownPeriod: 120
minReplicaCount: 3
maxReplicaCount: 40
triggers:
- type: redis
metadata:
address: redis.n8n.svc.cluster.local:6379
listName: bull:jobs:wait
listLength: '20'
activationListLength: '5'listLength: 20— one extra worker per 20 queued jobs.activationListLength: 5— don't wake workers for tiny bursts.cooldownPeriod: 120— wait 2 min before scaling down; avoids thrash during bursty traffic.
If workers OOM under load, cap concurrency per worker via N8N_CONCURRENCY_PRODUCTION_LIMIT and let KEDA add replicas instead — vertical scaling is a trap here.
Postgres — HA, sizing, and the queries that will bite you
- Managed (Cloud SQL / RDS / Aiven) is the default answer. Self-managed via CloudNativePG is fine if you already operate Postgres.
- Same region, same AZ or adjacent — network latency to Postgres dominates workflow p95.
- Storage: budget 1–5GB per 100k executions with default retention. Executions are the storage hog.
- Set
EXECUTIONS_DATA_PRUNE=trueandEXECUTIONS_DATA_MAX_AGE=336(14 days) — or you'll wake up to a full disk. - Enable
pg_stat_statements; the n8n execution history queries are the usual slow-query culprits.
For the deeper Postgres playbook see Postgres for product engineers.
Redis — small, hot, and boring
- Redis is only a queue here — no persistence required, but enable AOF appendonly for safety.
- 1–2 GB memory is plenty for most deployments; alert at 70%.
- Managed Redis is fine; Sentinel or Cluster only if you already run them.
- Never point n8n at a shared Redis with other apps — noisy-neighbor issues are painful to debug.
Binary data — S3, never the database
Storing binary data in Postgres bloats the DB, slows every query, and makes backups painful. Configure S3 mode from day one:
env:
N8N_DEFAULT_BINARY_DATA_MODE: s3
N8N_EXTERNAL_STORAGE_S3_BUCKET_NAME: n8n-binary-prod
N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION: eu-west-1
N8N_EXTERNAL_STORAGE_S3_ACCESS_KEY: <from-secret>
N8N_EXTERNAL_STORAGE_S3_ACCESS_SECRET: <from-secret>- Lifecycle rule: expire binary objects after 30 days unless a workflow tags them for longer retention.
- Bucket policy: private, server-side encryption on, versioning on.
- If regulated: object lock in compliance mode; matches the compliance blueprint in workflow automation blueprints.
Secrets, credentials, and the encryption key
n8n encrypts every stored credential with a single symmetric key (N8N_ENCRYPTION_KEY). Lose it and every credential in the database is unrecoverable — you re-enter them all by hand.
- Store the key in AWS Secrets Manager / GCP Secret Manager / Vault. Never commit, never bake into an image.
- Sync into the cluster via External Secrets Operator.
- Snapshot the key alongside the DB backup, encrypted with a separate KMS key. See the backups section.
- Rotation is possible but painful — you re-encrypt every credential. Document the runbook; rehearse it once so it isn't the first time in a real incident.
Backups that actually restore
- Nightly logical backup —
pg_dumpor your managed DB's export, uploaded to object storage with 30-day retention. - Continuous WAL archiving for PITR — restore to any point in the last 7 days.
- Encryption-key backup — versioned copy in a separate KMS/account, protected by IAM policy.
- Binary data — bucket versioning + cross-region replication for the S3 bucket.
- Monthly game day — restore into a scratch namespace, boot n8n, run a smoke workflow. Time it. Document it. If you skip this, your backup is fiction.
Zero-downtime upgrades
- Read the n8n changelog for the target version — flag any breaking node changes (Code node, Function node, HTTP Request behavior).
- Bump image tag in a staging cluster; run the full smoke workflow suite.
- In prod: roll workers first with a max-surge=1 rolling update. KEDA will replace them one at a time.
- Drain the webhook deployment behind a load balancer; roll it.
- Roll main last. Because main is leader-elected, expect a 5–10s cron gap — schedule the roll outside cron-critical windows.
Observability — the metrics that catch real problems
- n8n Prometheus metrics — enable
N8N_METRICS=true; scrape/metrics. - Queue depth (Redis
LLEN bull:jobs:wait) — if it climbs faster than workers scale, you have a bottleneck downstream (usually a slow HTTP integration). - Execution p95 duration per workflow — regressions after a deploy are your #1 signal.
- Failed executions rate — pair with the DLQ pattern from n8n error handling.
- Postgres connections — n8n opens a lot; use PgBouncer in transaction mode.
- Node OOM / restart count — sustained restarts mean per-execution memory limits are too tight or one workflow leaks.
Bridge into OTLP tracing per execution; see observability for AI systems for the tracing recipes.
Security hardening
- Terminate TLS at the ingress; force HTTPS.
- Basic auth or SSO in front of the editor UI — the editor is not designed to be exposed to the internet.
- Network policies: workers can talk to Redis + Postgres + egress; the editor is internal-only.
- Pin runners (
N8N_RUNNERS_ENABLED=true) to isolate Code/Function-node execution. - Rotate DB and Redis credentials quarterly via External Secrets.
- Log every credential-touch event to your SIEM; the audit trail matters when things go wrong.
Cost — what a real deployment actually spends
| Component | Small (10k exec/day) | Medium (100k) | Large (1M+) |
|---|---|---|---|
| main + webhook | $40 | $120 | $400 |
| worker (autoscaled) | $80 | $400 | $2,000 |
| Postgres (managed HA) | $120 | $300 | $900 |
| Redis (managed) | $40 | $80 | $250 |
| S3 (binary + backups) | $10 | $60 | $400 |
| Egress / LB | $30 | $100 | $500 |
| Total (USD/month) | ~$320 | ~$1,060 | ~$4,450 |
Compare to Cloud pricing at those tiers before committing — self-hosting wins on scale, not on small deployments.
Failure modes and their fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Queue depth grows despite workers scaling | Downstream integration slow / rate-limited | Add per-workflow concurrency cap; retry with jitter |
| Workers OOM under load | One heavy workflow | Cap worker concurrency; split the workflow |
| Main pod restart loop | DB connection storm on boot | PgBouncer in transaction mode |
| Execution history query slow | Missing prune / no indexes | Enable prune; add index on executed_at |
| Credentials can't decrypt after restore | Encryption key mismatch | Restore key from KMS backup |
| Cron jobs firing twice | Two mains without leader lock | Ensure main has leader election enabled |
A production checklist you can print
- Queue mode on; main/webhook/worker split into three deployments.
- KEDA autoscaling workers on Redis queue depth.
- Postgres HA (managed or CloudNativePG) with PgBouncer.
- Redis with AOF, dedicated to n8n.
- S3 for binary data with lifecycle + versioning.
- External Secrets for all credentials; encryption key in KMS with backup.
- Nightly logical backup + WAL PITR; monthly restore drill scheduled.
- Prometheus metrics scraped; queue depth + p95 dashboards live.
- OTLP tracing bridged into your central observability plane.
- Ingress with TLS, SSO on the editor, network policies enforced.
- Runbook for encryption-key rotation rehearsed once end-to-end.
Where to go next
Frequently asked
Docker Compose or Kubernetes?
Compose is fine for a single node and low volume — cheap, easy, boring. Move to Kubernetes when you need HA, autoscaling, or your team already operates it. Don't adopt K8s for n8n alone.
What Postgres size do I need?
Executions dominate. Budget ~1–5 GB per 100k executions at default retention, prune to 14 days, and monitor the pruning job — a stalled pruner fills the disk quietly.
Do I need queue mode below 10k executions/day?
Not strictly, but queue mode makes upgrades and scaling much less painful. If you expect growth, ship it in queue mode from day one — retrofitting later is more work than starting there.
How do I handle a lost N8N_ENCRYPTION_KEY?
You don't — you re-enter every credential by hand. Backup the key in a separate KMS/account, rehearse the restore, and treat the key with the same care as your root DB password.
Can I run everything in one cluster with other apps?
Yes — dedicate a namespace, apply resource quotas, and use network policies. But keep Redis dedicated to n8n; noisy-neighbor Redis issues are a nightmare to debug.
How do I test-drill a restore without touching prod?
Spin up a scratch namespace, point a fresh n8n install at a restored Postgres + a fresh Redis, load the KMS-backed encryption key, and run a smoke workflow. Time it end-to-end. Do this monthly.
Building something similar?
I help teams ship production-grade AI agents, n8n workflows, and data platforms. Let's talk about what you're building.
Work with me
Islam Gamal
AI, Data & Automation Engineer. I design and ship production AI agents, n8n workflows, and cloud data platforms — with a focus on reliability, cost, and measurable business impact. Founder of Tashghil and Tek bil Arabi.
More from Islam Gamal
Building Production AI Agents with n8n: Architecture, Guardrails, Evals, and Cost Control
The full n8n agent playbook — reference architecture, memory design, tool routing, JSON-schema guardrails, retries, DLQs, evals, observability, and the small handful of decisions that keep the token bill sane at scale.
The n8n Error Handling Playbook: Retries, DLQs, Circuit Breakers, and Replay
The full production playbook for n8n reliability: how to classify failures, design retry policies that don't melt upstreams, build a Postgres DLQ with safe replay, run circuit breakers per integration, and wire the observability that catches incidents before your customers do.
Workflow Automation Blueprints: 12 Patterns Every Team Ships (and the Envelope That Ties Them Together)
The 12 automation blueprints I reuse across every client — lead routing, invoice processing, content ops, incident response — with the exact triggers, guards, envelopes, DLQs, and human-in-the-loop patterns that make them survive production.
Browse every article by Islam Gamal on the author page.