Skip to content

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.

encryptionKey protects all stored credentials — losing it invalidates every workflow connection

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 — tar for SQLite, pg_dump for PostgreSQL
  • Explicit listener contracts — configurable HTTP, task broker, and runner health ports
  • Least-privilege pod identity — Kubernetes API token mounting is disabled by default

Architecture and Listener Contracts

The main Deployment serves the editor and webhooks through containerPorts.http. In external task-runner mode, each n8n process hosts a broker on containerPorts.runnerBroker; the same-pod runner sidecar exposes its health server on containerPorts.runnerHealth. The chart wires the supported n8n environment variables and runner URI to these values, while service.port remains the independent Kubernetes-facing port.

SQLite is a single-main-pod deployment. PostgreSQL is required before enabling Redis queue mode and worker Deployments. Main and worker pods share the same encryption key and task-runner authentication Secret.

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-credentials

Configuration 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.44.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.44.4).
taskRunners.image.pullPolicy string IfNotPresent External task-runner image pull policy.
taskRunners.extraEnv array [] Extra environment variables for runner sidecars in main and worker pods.
taskRunners.nativePython.enabled bool false Enable native Python support with a compatible external runner image.

Use taskRunners.extraEnv for runner-container settings such as GENERIC_TIMEZONE, NODE_OPTIONS, or N8N_RUNNERS_MAX_OLD_SPACE_SIZE:

taskRunners:
  extraEnv:
    - name: GENERIC_TIMEZONE
      value: UTC

In external mode, Code node module allowlists such as NODE_FUNCTION_ALLOW_BUILTIN and NODE_FUNCTION_ALLOW_EXTERNAL must instead be configured as env-overrides in /etc/n8n-task-runners.json inside the runner image. Container environment variables do not configure the processes launched by the runner launcher. See the upstream task runner configuration.

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).

Listener Ports

Parameter Type Default Description
containerPorts.http integer 5678 Main n8n process HTTP listener.
containerPorts.runnerBroker integer 5679 Broker listener used by external task runners.
containerPorts.runnerHealth integer 5680 External runner sidecar health-check listener.
service.port integer 80 Kubernetes Service port; does not alter runtime.

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.standalone.persistence.size string 8Gi PVC size for PostgreSQL.
redis.enabled boolean false Deploy the bundled Redis subchart.
redis.standalone.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/readiness.
resources object {} CPU and memory requests/limits.
extraManifests array [] Extra Kubernetes manifests.
serviceAccount.automountServiceAccountToken boolean false Mount the Kubernetes API token into main and worker pods.

Security and Production Boundaries

The workload runs as the upstream non-root user, drops all Linux capabilities, denies privilege escalation, uses RuntimeDefault seccomp, and does not mount a Kubernetes API token by default. Preserve the encryption key across every upgrade and restore. Namespace administrators and readers of Helm release state remain inside the credential trust boundary.

The chart does not provision DNS, TLS issuers, routing controllers, external PostgreSQL or Redis, object storage, NetworkPolicy, or monitoring controllers. If the task broker is reachable outside its pod, restrict the broker Service port configured by containerPorts.runnerBroker (5679 by default) to trusted runner clients with platform-managed network policy. service.port controls the separate HTTP Service port.

Troubleshooting

  • If the editor or readiness probe fails after a port change, verify that containerPorts.http matches N8N_PORT in the rendered main container.
  • If Code nodes cannot reach their runner, compare containerPorts.runnerBroker, N8N_RUNNERS_BROKER_PORT, and N8N_RUNNERS_TASK_BROKER_URI in the same pod.
  • If the runner is unhealthy, compare containerPorts.runnerHealth with N8N_RUNNERS_LAUNCHER_HEALTH_CHECK_PORT and inspect the runner logs.
  • If workers wait indefinitely, inspect PostgreSQL, Redis, and wait-for-main init-container logs together.
  • If restored credentials cannot be decrypted, restore the original encryption key before starting n8n.

Upgrade Notes

n8n 2.44.4 includes the 2.42 through 2.44 maintenance lines and fixes duplicate Chat Hub segment handling in queue mode and deferred tool-call resumption for tools loaded by a skill. Agents remain enabled by default; this does not add a Kubernetes workload or RBAC requirement. 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.44.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.

The dependency set uses PostgreSQL 2.1.1 and Redis 3.1.0. Review each intervening release from 2.39.0 through 2.44.4. For source-control HTTPS remotes with a private CA, GIT_SSL_CAINFO is now honored; mount the trust file and pass its path through the existing environment extension when needed.

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.

The chart defaults to N8N_RUNNERS_MODE=external. It creates a shared auth token, opens the broker port, and runs a docker.io/n8nio/runners sidecar next to the main pod and each queue worker. This avoids the missing-Python warning produced by internal runner mode in the upstream n8nio/n8n image and gives each queue worker its own runner, as required by n8n external task runner architecture.

Anonymous diagnostics are disabled by default with N8N_DIAGNOSTICS_ENABLED=false, which keeps self-hosted clusters private and reduces startup log noise. Operators can opt in with n8n.diagnosticsEnabled=true.

More Information