n8n
Deploy n8n on Kubernetes — a workflow automation platform with a visual editor, 200+ integrations, and code nodes. Supports SQLite (zero-configuration default), and PostgreSQL, with Redis-backed queue mode for horizontal scaling of workflow executions.
n8n.encryptionKey encrypts OAuth tokens, API keys, and passwords stored in workflow credentials. If the key changes
(including during reinstall with a new auto-generated key), all saved credentials in all workflows become
permanently unreadable. Always persist it via encryptionKey.existingSecret and never rotate it in production.
Key Features
- Two database backends — SQLite (default) and PostgreSQL
- Queue mode — Redis-backed horizontal scaling with separate worker Deployments
- Encryption key protection — all workflow credentials encrypted at rest
- Webhook URL required — must match the external-facing URL for triggers to work
- Database-aware backup —
tarfor SQLite,pg_dumpfor PostgreSQL
Installation
HTTPS repository:
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install n8n helmforge/n8n -f values.yaml
OCI registry:
helm install n8n oci://ghcr.io/helmforgedev/helm/n8n -f values.yaml
Deployment Examples
# values.yaml — n8n with SQLite (zero database configuration)
n8n:
webhookUrl: 'https://n8n.example.com' # required for webhook triggers
encryptionKey:
existingSecret: n8n-master-secret # key: encryption-key (never rotate)
existingSecretKey: encryption-key
persistence:
enabled: true
size: 10Gi # SQLite DB + installed custom nodes
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: n8n.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: n8n-tls
hosts:
- n8n.example.com# values.yaml — n8n with bundled PostgreSQL
n8n:
webhookUrl: 'https://n8n.example.com'
logLevel: info
encryptionKey:
existingSecret: n8n-master-secret
postgresql:
enabled: true
auth:
database: n8n
username: n8n
password: 'strong-db-password'
primary:
persistence:
enabled: true
size: 20Gi
persistence:
enabled: true
size: 5Gi # custom nodes (no SQLite when PostgreSQL is active)
ingress:
enabled: true
ingressClassName: traefik
hosts:
- host: n8n.example.com
paths:
- path: /
pathType: Prefix# values.yaml — n8n in queue mode with 3 workers
# Queue mode separates the main pod (editor + triggers) from worker pods (executions)
n8n:
webhookUrl: 'https://n8n.example.com'
encryptionKey:
existingSecret: n8n-master-secret
postgresql:
enabled: true
auth:
password: 'strong-db-password'
redis:
enabled: true
auth:
password: 'strong-redis-password'
queue:
enabled: true
workers: 3 # creates 3 separate worker Deployment pods
concurrency: 10 # workflows executed simultaneously per worker
persistence:
enabled: true
size: 5Gi
ingress:
enabled: true
ingressClassName: traefik
hosts:
- host: n8n.example.com
paths:
- path: /
pathType: Prefix# values.yaml — n8n with external PostgreSQL and daily backup
n8n:
webhookUrl: 'https://n8n.example.com'
encryptionKey:
existingSecret: n8n-master-secret
postgresql:
enabled: false
database:
mode: external
external:
vendor: postgres
host: postgres.database.svc.cluster.local
name: n8n
username: n8n
existingSecret: n8n-db-credentials
existingSecretPasswordKey: database-password
persistence:
enabled: true
size: 5Gi
backup:
enabled: true
schedule: '0 3 * * *'
s3:
endpoint: https://s3.amazonaws.com
bucket: n8n-backups
existingSecret: n8n-s3-credentialsConfiguration Reference
Core
| Parameter | Type | Default | Description |
|---|---|---|---|
nameOverride |
string | "" |
Override the chart name. |
fullnameOverride |
string | "" |
Override the full release name. |
commonLabels |
object | {} |
Extra labels added to all resources. |
Image
| Parameter | Type | Default | Description |
|---|---|---|---|
image.repository |
string | docker.io/n8nio/n8n |
n8n container image. |
image.tag |
string | "2.38.4" |
Image tag. |
image.pullPolicy |
string | IfNotPresent |
Image pull policy. |
Task Runners
| Parameter | Type | Default | Description |
|---|---|---|---|
taskRunners.mode |
string | external |
Run the dedicated task-runner sidecar. |
taskRunners.image.repository |
string | docker.io/n8nio/runners |
External task-runner image repository. |
taskRunners.image.tag |
string | "" |
Runner tag; empty inherits image.tag (2.38.4). |
taskRunners.image.pullPolicy |
string | IfNotPresent |
External task-runner image pull policy. |
n8n Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
n8n.encryptionKey |
string | "" |
Credential encryption key. Auto-generated if empty. Use existingSecret. |
n8n.webhookUrl |
string | "" |
Full external URL for webhook triggers. Required for all webhook nodes. |
n8n.editorBaseUrl |
string | "" |
Editor base URL. Auto-detected from Ingress if empty. |
n8n.logLevel |
string | info |
Log level: silent, error, warn, info, debug, verbose. |
n8n.logOutput |
string | console |
Log output destination: console or file. |
n8n.extraEnv |
array | [] |
Extra environment variables (e.g. GENERIC_TIMEZONE, N8N_DIAGNOSTICS_ENABLED). |
Encryption Key
| Parameter | Type | Default | Description |
|---|---|---|---|
encryptionKey.existingSecret |
string | "" |
Existing secret containing the encryption key. |
encryptionKey.existingSecretKey |
string | encryption-key |
Key for the encryption key in the existing secret. |
Queue Mode
| Parameter | Type | Default | Description |
|---|---|---|---|
queue.enabled |
boolean | false |
Enable Redis-backed queue mode (main pod + separate worker Deployments). |
queue.workers |
integer | 1 |
Number of worker replica Deployments. |
queue.concurrency |
integer | 10 |
Simultaneous workflow executions per worker. |
queue.resources |
object | {} |
CPU and memory requests/limits for worker pods. |
queue.external.host |
string | "" |
External Redis host (alternative to subchart). |
queue.external.existingSecret |
string | "" |
Existing secret for external Redis password. |
Database
Auto-detection precedence (database.mode: auto):
| Priority | Condition | Result |
|---|---|---|
| 1 | database.external.host or external.existingSecret |
External DB |
| 2 | postgresql.enabled: true |
PostgreSQL subchart |
| 3 | None of the above | SQLite (default) |
| Parameter | Type | Default | Description |
|---|---|---|---|
database.mode |
string | auto |
Database mode: auto, sqlite, external, postgresql. |
database.sqlite.file |
string | /home/node/.n8n/database.sqlite |
SQLite file path inside the data volume. |
database.external.vendor |
string | postgres |
External DB vendor: postgres only. |
database.external.host |
string | "" |
External database hostname. |
database.external.existingSecret |
string | "" |
Existing secret with database password. |
database.external.existingSecretPasswordKey |
string | database-password |
Key for the password. |
Subcharts
| Parameter | Type | Default | Description |
|---|---|---|---|
postgresql.enabled |
boolean | false |
Deploy the bundled PostgreSQL subchart. |
postgresql.auth.password |
string | "" |
Password. Auto-generated if empty. |
postgresql.primary.persistence.size |
string | 8Gi |
PVC size for PostgreSQL. |
redis.enabled |
boolean | false |
Deploy the bundled Redis subchart. |
redis.primary.persistence.size |
string | 1Gi |
PVC size for Redis. |
Persistence
| Parameter | Type | Default | Description |
|---|---|---|---|
persistence.enabled |
boolean | true |
Enable PVC for /home/node/.n8n (SQLite DB + custom nodes). |
persistence.size |
string | 5Gi |
PVC size. |
persistence.storageClass |
string | "" |
StorageClass for the PVC. |
persistence.existingClaim |
string | "" |
Use an existing PVC. |
Backup
Backup mode is automatically selected based on the active database vendor.
| Parameter | Type | Default | Description |
|---|---|---|---|
backup.enabled |
boolean | false |
Enable scheduled S3 backup CronJob. |
backup.schedule |
string | "0 3 * * *" |
Cron schedule. |
backup.archivePrefix |
string | n8n |
Prefix for backup archive filenames. |
backup.s3.endpoint |
string | "" |
S3-compatible endpoint URL. |
backup.s3.bucket |
string | "" |
Target bucket name. |
backup.s3.existingSecret |
string | "" |
Existing secret with S3 credentials. |
backup.database.postgresDumpArgs |
string | "" |
Extra arguments for pg_dump. |
Service, Ingress, Probes
| Parameter | Type | Default | Description |
|---|---|---|---|
service.type |
string | ClusterIP |
Service type. |
ingress.enabled |
boolean | false |
Enable an Ingress resource. |
ingress.ingressClassName |
string | "" |
Ingress class name. |
startupProbe.enabled |
boolean | true |
Startup probe on /healthz. |
livenessProbe.enabled |
boolean | true |
Liveness probe on /healthz. |
readinessProbe.enabled |
boolean | true |
Readiness probe on /healthz. |
resources |
object | {} |
CPU and memory requests/limits. |
extraManifests |
array | [] |
Extra Kubernetes manifests. |
Upgrade Notes
n8n 2.38.4 includes the 2.36–2.38 runner broker and shutdown fixes,
database pool recovery, encryption-key seeding and queue execution fixes. Back
up the database and data volume, preserve the encryption key, and validate
workflows and credentials in staging before upgrading. Keep the app and external
runner tags aligned; an empty taskRunners.image.tag inherits image.tag.
Use helm upgrade --reset-then-reuse-values with image.tag=2.38.4 and update
any separately pinned runner tag. Existing generated encryption keys and runner
tokens are retained through Helm lookup; use existing Secrets for offline
rendering or GitOps. Automatic database migrations require a recoverable backup.
SQLite updates now use Recreate to stop the old main process before the new
version opens the same database. Plan for brief downtime. The Python toggle
taskRunners.nativePython.enabled now controls the n8n 2.x variable
N8N_PYTHON_ENABLED; it remains disabled by default and requires external runners.
MySQL and MariaDB storage migration
n8n removed MySQL/MariaDB storage in version 2.0. Earlier chart releases still
exposed these unsupported options. This chart now rejects mysql.enabled=true,
database.mode=mysql and non-PostgreSQL external vendors, and no longer bundles
MySQL or MySQL backup jobs. The MySQL workflow node is unaffected.
If an older deployment uses MySQL/MariaDB, stop here and migrate its data using
the upstream migration procedure on a compatible source release. Back up the
database and encryption key, migrate into a separate PostgreSQL instance, and
verify credentials, workflows and executions before switching traffic. Changing
database.mode alone does not migrate data. Do not apply this release over an
existing bundled MySQL installation until its data has been migrated and its
backup and PVC recovery have been verified.
See the 2.0 storage migration guidance and upstream releases.