Production Grade N8N Deployment on Kubernetes

Production Grade N8N Deployment on Kubernetes

Introduction to n8n

N8N is an open source workflow automation platform that allows organizations to connect applications, services, databases, APIs, and infrastructure components through automated workflows. It provides a visual workflow designer where users build automation pipelines using nodes, where each node performs a specific task such as reading data, calling APIs, sending notifications, processing files, or interacting with cloud services. Unlike traditional automation tools, n8n supports custom JavaScript, API integrations, and extensibility, making it suitable for both simple and advanced automation use cases.

Why We Use n8n

Organizations use n8n to automate repetitive tasks, reduce manual effort, and integrate systems without building custom integration services. Common use cases include:
• Automating ticket creation from emails
• Syncing data between applications
• Sending alerts and notifications
• Processing files and documents
• Managing infrastructure tasks
• Integrating AI services and workflows
• Automating approval workflows
• Monitoring alerts and DevOps automation
• Kubernetes and cloud resource automation
• Backup and deployment workflows

Read More: Why N8N Matters for Modern Businesses: Benefits, Use Cases & ROI Explained

Why Deploy n8n on Kubernetes

Kubernetes provides a production ready environment for running n8n because it offers high availability, self healing, scaling, rolling updates, and resource management. In Kubernetes, failed pods are automatically restarted and workloads can scale based on demand. It also integrates easily with PostgreSQL, Redis, ingress controllers, and secret management tools, making it ideal for enterprise deployments.

Production Architecture Overview

A production n8n deployment uses Queue Mode architecture instead of a single pod setup to properly separate workload responsibilities and improve system stability. This architecture separates responsibilities so that main n8n pods are responsible for handling the user interface, API requests, and workflow orchestration, while Worker pods are responsible for executing workflows asynchronously by consuming jobs from the Redis queue, ensuring heavy processing does not impact the main application. Webhook pods handle incoming external HTTP requests and triggers, allowing fast and isolated processing of inbound events.

PostgreSQL is used as the primary database to store workflows, credentials, and execution history in a persistent and consistent manner. Redis acts as the queue layer between the main and worker pods, temporarily holding workflow jobs until workers pick them up for execution. An ingress controller is used to expose the application securely to external users. This overall design improves scalability, performance, and reliability in production environments.

Prerequisites

Before starting the deployment, the environment should be properly prepared. A running Kubernetes cluster with kubectl access is required. A domain name should be available for secure external access. An ingress controller such as Traefik or a Gateway API implementation like Envoy Gateway should already be installed. A PostgreSQL database, either managed or self-hosted, and a Redis instance must also be available. Finally, cert-manager is recommended for managing TLS certificates and enabling secure HTTPS communication.

Deploying PostgreSQL Database

PostgreSQL is a core dependency for n8n because it stores all workflows, execution data, and configuration in a persistent and reliable way. It ensures data consistency and prevents loss of information during scaling, restarts, or failures, making it essential for production deployments.

First, create a namespace and verify storage class availability.

kubectl create namespace n8n
kubectl get sc

Then add the Helm repository and update it.

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

Create PostgreSQL secret:

kubectl create secret generic postgres-secret \
  --from-literal=postgres-password='postgres123' \
  --from-literal=password='n8n123' \
  -n n8n
kubectl get secret postgres-secret -n n8n

Create PostgreSQL values file:

auth:
  existingSecret: postgres-secret
  secretKeys:
    adminPasswordKey: postgres-password
    userPasswordKey: password
  username: n8n
  database: n8n
primary:
  persistence:
    enabled: true
    size: 20Gi
    storageClass: ebs-sc

Install PostgreSQL using Helm:

helm install postgres bitnami/postgresql \
  -n n8n \
  -f postgres-values.yaml

Finally verify deployment:

kubectl get pods -n n8n
kubectl get pvc -n n8n

At this stage, PostgreSQL should be running and storage should be successfully bound as show in image.

 PostgreSQL will expose a service usually postgres-postgresql as show in above image which will be used as a POSTGRESDB_HOST in the n8n configuration.

Deploying Redis (Queue Backend)

Redis is a core component in production n8n deployments when using Queue Mode. It acts as a high-speed in-memory message broker (queue system) between the n8n main instance and worker pods. When a workflow triggered, the job is pushed into Redis, and worker pods continuously pull tasks from Redis to execute them. This decouples execution from the main application and improves scalability, reliability, and performance. This allows the main n8n instance to remain lightweight while worker pods handle execution independently.

Create Redis secret:

kubectl create secret generic redis-secret \
  --from-literal=redis-password='redis123' \
  -n n8n
kubectl get secret redis-secret -n n8n

Create Redis values file:

auth:
  enabled: true
  existingSecret: redis-secret
  existingSecretPasswordKey: redis-password
master:
  persistence:
    enabled: true
    storageClass: ebs-sc
    size: 5Gi
replica:
  persistence:
    enabled: true
    storageClass: ebs-sc
    size: 5Gi

Install Redis using Helm:

helm install redis bitnami/redis \
  -n n8n \
  -f redis-values.yaml

Verify Redis deployment:

kubectl get pods -n n8n
kubectl get svc -n n8n
kubectl get pvc -n n8n

Redis will expose a service usually redis-master as show in above image which will be used as a REDIS_HOST in the n8n configuration.

Deploying n8n

Helm simplifies deployment and lifecycle management of Kubernetes applications. In production, PostgreSQL and Redis should ideally be external or highly available services for better reliability. n8n must run in Queue Mode so that workflow execution is separated from the main application layer. In the values file, we define separate configurations for main, worker, and webhook pods to support this architecture, where the main pod handles the application interface and API, worker pods process workflow executions asynchronously from Redis queue jobs, and webhook pods manage incoming external triggers independently for improved scalability and isolation.

Create secret for n8n encryption key:

# Generate encryption key

N8N_KEY=$(openssl rand -hex 32)

# Verify generated key

echo "Generated Key: $N8N_KEY"

# Create Kubernetes Secret

kubectl create secret generic n8n-encryption-key \

  --from-literal=key="$N8N_KEY" \

  -n n8n

Create a values file for n8n deployment.

main:

  replicaCount: 1

  resources:

    requests:

      cpu: "300m"

      memory: "768Mi"

    limits:

      cpu: "1000m"

      memory: "1.5Gi"

  config:

    DB_TYPE: postgresdb

    DB_POSTGRESDB_HOST: postgres-postgresql

    DB_POSTGRESDB_PORT: 5432

    DB_POSTGRESDB_DATABASE: n8n

    DB_POSTGRESDB_USER: n8n

    EXECUTIONS_MODE: queue

    QUEUE_BULL_REDIS_HOST: redis-master

    QUEUE_BULL_REDIS_PORT: 6379

    QUEUE_BULL_REDIS_DB: 0

    N8N_HOST: n8n.yourdomain.com

    N8N_PROTOCOL: https

    WEBHOOK_URL: https://n8n.yourdomain.com

    N8N_PORT: "5678"

    GENERIC_TIMEZONE: Asia/Karachi

  extraEnv:

    - name: DB_POSTGRESDB_PASSWORD

      valueFrom:

        secretKeyRef:

          name: postgres-secret

          key: password

    - name: N8N_ENCRYPTION_KEY

      valueFrom:

        secretKeyRef:

          name: n8n-encryption-key

          key: key

    - name: QUEUE_BULL_REDIS_PASSWORD

      valueFrom:

        secretKeyRef:

          name: redis-secret

          key: redis-password

    - name: N8N_RUNNERS_ENABLED

      value: "true"

    - name: N8N_PORT

      value: "5678"

webhook:

  enabled: true

  replicaCount: 2

  extraEnv:

    - name: DB_POSTGRESDB_HOST

      value: postgres-postgresql

    - name: DB_POSTGRESDB_PORT

      value: "5432"

    - name: DB_POSTGRESDB_DATABASE

      value: n8n

    - name: DB_POSTGRESDB_USER

      value: n8n

    - name: DB_POSTGRESDB_PASSWORD

      valueFrom:

        secretKeyRef:

          name: postgres-secret

          key: password

    - name: N8N_ENCRYPTION_KEY

      valueFrom:

        secretKeyRef:

          name: n8n-encryption-key

          key: key

    - name: QUEUE_BULL_REDIS_PASSWORD

      valueFrom:

        secretKeyRef:

          name: redis-secret

          key: redis-password

    - name: N8N_PORT

      value: "5678"

worker:

  enabled: true

  replicaCount: 3

  resources:

    requests:

      cpu: "300m"

      memory: "768Mi"

    limits:

      cpu: "1500m"

      memory: "2Gi"

  extraEnv:

    - name: DB_POSTGRESDB_HOST

      value: postgres-postgresql

    - name: DB_POSTGRESDB_PORT

      value: "5432"

    - name: DB_POSTGRESDB_DATABASE

      value: n8n

    - name: DB_POSTGRESDB_USER

      value: n8n

    - name: DB_POSTGRESDB_PASSWORD

      valueFrom:

        secretKeyRef:

          name: postgres-secret

          key: password

    - name: N8N_ENCRYPTION_KEY

      valueFrom:

        secretKeyRef:

          name: n8n-encryption-key

          key: key

    - name: QUEUE_BULL_REDIS_PASSWORD

      valueFrom:

        secretKeyRef:

          name: redis-secret

          key: redis-password

    - name: N8N_RUNNERS_ENABLED

      value: "true"

    - name: N8N_PORT

      value: "5678"

  livenessProbe: null

  readinessProbe: null

service:

  type: ClusterIP

persistence:

  enabled: true

  storageClass: ebs-sc

  size: 10Gi

Replace n8n.yourdomain.com with your actual domain name.

Install n8n using Helm:

helm install n8n \

  oci://8gears.container-registry.com/library/n8n \

  -n n8n \

  -f n8n-values.yaml

Verify n8n deployment:

kubectl get pods -n n8n

kubectl get svc -n n8n

External Access Using Gateway API and HTTPRoute

To expose n8n to the internet, Envoy Gateway is installed in the Kubernetes cluster as a Gateway API controller. A hostname like n8n.company.com is used, and DNS is pointed to the cluster load balancer. Envoy Gateway receives external traffic on the ports defined in the Gateway and forwards it inside the cluster based on rules defined in the HTTPRoute. The Gateway defines how traffic enters the cluster, while the HTTPRoute defines where the traffic should go, such as the n8n service. For production, TLS should be enabled using cert-manager with Let’s Encrypt to secure communication over HTTPS.

Verify Gateway Api

Verify Service Name

kubectl get svc -n n8n

Create file httproute.yaml

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  name: n8n-route

  namespace: n8n

spec:

  parentRefs:

    - name: eg

      namespace: default

  hostnames:

    - n8n.yourdomain.com

  rules:

    - matches:

        - path:

            type: PathPrefix

            value: /

      backendRefs:

        - name: n8n

          port: 80

kubectl apply -f httproute.yaml


Verify HTTP route

kubectl get httproute -n n8n

Verify via domain in browser

Autoscaling Strategies

Kubernetes allows scaling based on workload demand. In n8n, worker pods should be scaled dynamically because they handle workflow execution from a Redis queue. The Horizontal Pod Autoscaler can be used for CPU and memory based scaling, but it is not ideal for queue driven systems because CPU usage does not always reflect pending work. In n8n, jobs can accumulate in the Redis queue while CPU usage remains low because workers are idle until they pick up tasks. This means HPA may react late or not scale at all during high queue pressure. A better strategy in such cases is event driven autoscaling using KEDA, which provides more accurate scaling behavior for asynchronous workloads.

KEDA Deployment

KEDA is more efficient in this scenario because it scales based on Redis queue depth, which directly represents the actual workload waiting to be processed. When the queue size increases, KEDA automatically increases the number of worker pods, and when the queue becomes empty or decreases, it scales them back down. This makes scaling faster, more accurate, and better aligned with real workload demand compared to CPU based autoscaling. This scaling is applied only to the n8n worker deployment, not the main n8n application. The main pods remain fixed because they handle the UI and API layer, which are not workload queue based and do not require dynamic scaling.

Note: KEDA automatically creates and manages a Kubernetes HPA and feeds it external metrics like Redis queue depth. The HPA then uses these metrics to scale worker pods dynamically in real time based on workload demand.

Add Helm Repo

helm repo add kedacore https://kedacore.github.io/charts 
helm repo update 

Install KEDA using Helm

helm install keda kedacore/keda \ 
  --namespace keda \ 
  --create-namespace 

Verify KEDA Installation

kubectl get pods -n keda 

Trigger Authentication
Create a Trigger Authentication resource. This resource allows KEDA to access the Redis password stored in the Kubernetes secret and use it for authentication when monitoring the Redis queue.

Create file trigger-auth.yaml

apiVersion: keda.sh/v1alpha1 

kind: TriggerAuthentication 

metadata: 

  name: redis-auth 

  namespace: n8n 

spec: 

  secretTargetRef: 

    - parameter: password 

      name: redis-secret 

      key: redis-password 

Apply TriggerAuthentication

kubectl apply -f trigger-auth.yaml

Scaled Object

In this step, we will create a Scaled Object that is responsible for scaling n8n worker pods up and down based on the queue workload. Before creating the ScaledObject, verify the number of running worker pods.

kubectl get pods -n n8n

According to the output, 3 worker pods are currently running.

Create file keda-scaledobject.yaml

apiVersion: keda.sh/v1alpha1 

kind: ScaledObject 

metadata: 

  name: n8n-worker-scaledobject 

  namespace: n8n 

spec: 

  scaleTargetRef: 

    name: n8n-worker 

  minReplicaCount: 1 

  maxReplicaCount: 7 

  pollingInterval: 10 

  cooldownPeriod: 30 

  triggers: 

    - type: redis 

      metadata: 

        address: redis-master.n8n.svc.cluster.local:6379 

        listName: bull 

        listLength: "5" 

      authenticationRef: 

        name: redis-auth

Apply ScaledObject

kubectl apply -f keda-scaledobject.yaml 

Verification for ScaledObject

kubectl get scaledobject -n n8n  
kubectl get pods -n n8n

Current Scaling Status
After enabling KEDA autoscaling, the n8n deployment is now running with replicas based on the configured scaling rules.
Now we can verify that earlier we had 3 worker pods, but now only 1 worker pod is running because the load is low. KEDA has scaled it down based on current demand and is maintaining the minimum replica value defined in the ScaledObject.

High Availability and Reliability

A production setup should always avoid single points of failure. Multiple replicas of n8n should run across nodes, and worker pods should be distributed across availability zones. Pod Disruption Budgets should be configured to ensure availability during maintenance. PostgreSQL and Redis should be deployed in high availability mode with persistent volumes (PV) to ensure data durability, along with proper backup and disaster recovery planning.

Conclusion

n8n is a powerful workflow automation platform that becomes even more powerful when deployed on Kubernetes. With Helm-based deployment, it gains scalability, self healing, and high availability. A production grade setup should always include Queue Mode architecture, external PostgreSQL and Redis, secure ingress with TLS, autoscaling using KEDA, and proper monitoring. Redis plays a key role as the high-speed queue layer that connects the main application with worker pods, ensuring fast, reliable, and scalable workflow execution. This ensures a fully production ready, resilient, and enterprise grade automation platform.