Learn n8n - Self-hosting & Deployment Patterns
Series/Learn n8n/Episode 15
Episode 15 of 23

Learn n8n - Self-hosting & Deployment Patterns

Learn how to deploy n8n yourself in production: Docker Compose with PostgreSQL and Redis, Kubernetes via Helm, cloud VMs with a reverse proxy, choosing storage backends and persistent data, up to high availability and scaling worker nodes with queue mode.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 14 you built governance for teams: roles, sharing, and audit. Now it's time for a permanent home. Throughout this series you've probably been running in desktop mode or a throwaway container — but production demands more: data must not be lost when a container restarts, the database must not become a point of failure, and load must be able to grow without a major overhaul.

Episode 15 covers Self-hosting & Deployment Patterns: deploying on Docker Compose, Kubernetes, and cloud VMs, choosing storage backends and persistent data, up to high availability and scaling worker nodes.

Choosing a Deployment Strategy

Before writing configuration files, decide on the stage first. Three common patterns:

  • Cloud VM — one or a few VMs where Docker runs. The simplest, suitable for small teams; you're responsible for OS updates, backups, and the reverse proxy.
  • Docker Compose — an evolution of a single VM: the whole stack (n8n, database, Redis, reverse proxy) is defined as a YAML file that can be committed to Git.
  • Kubernetes — for organizations already running a cluster. Offers auto-scaling, self-healing, and neat environment separation, at the cost of higher complexity.

The rule of thumb: start with Docker Compose on a single VM. Move up to Kubernetes only when your team or load genuinely needs orchestration — not because it's a trend.

Docker Compose for Production

A healthy production stack minimally consists of n8n, PostgreSQL as the database, and — if using queue mode — Redis as the message queue. Example of a complete composition:

docker-compose.yml - stack produksi n8n
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
 
  n8n:
    image: docker.n8n.io/n8nio/n8n
    ports:
      - "5678:5678"
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
      N8N_USER_MANAGEMENT_JWT_SECRET: ${N8N_USER_MANAGEMENT_JWT_SECRET}
      WEBHOOK_URL: https://n8n.example.com/
      GENERIC_TIMEZONE: Asia/Jakarta
      TZ: Asia/Jakarta
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - postgres
 
volumes:
  postgres_data:
  n8n_data:

Note three things: all secrets (POSTGRES_PASSWORD, N8N_ENCRYPTION_KEY, N8N_USER_MANAGEMENT_JWT_SECRET) come from .env or a secret store, not hardcoded. The n8n_data and postgres_data volumes guarantee data survives container re-creation. And WEBHOOK_URL is set to the public domain so webhook payloads use the correct URL behind the proxy.

Storage Backend, Database & Persistent Data

The database choice determines your instance's limits:

  • SQLite — default, enough for personal use and experiments. Not suitable for multi-user or queue mode because write access is concentrated in a single file.
  • PostgreSQL — the production recommendation: supports multi-user, concurrent execution, and is a prerequisite when running queue mode.

Besides the relational database, there's binary data (files downloaded or produced by workflows). By default n8n stores it in the filesystem within the n8n volume. For large-scale instances, storage mode can be directed to S3 with N8N_DEFAULT_BINARY_DATA_MODE=s3 along with its bucket credentials — moving file load from disk to object storage.

The key to all of it is persistence: every valuable state (database, binary data, .env) must live on restart-proof storage and be backed up periodically.

Deploying to a Cloud VM with a Reverse Proxy

A cloud VM is essentially a Docker host at the end of the day. What distinguishes it from a laptop: incoming traffic must have TLS terminated by a reverse proxy. Caddy is a good choice because it automatically issues Let's Encrypt certificates:

Caddyfile - proxy HTTPS ke n8n
n8n.example.com {
    reverse_proxy n8n:5678
}

Behind this proxy, WEBHOOK_URL in compose points to https://n8n.example.com/ so n8n builds correct webhook URLs. Also restrict port access in the security group: open only 80 and 443 to the public, and let 5678 be reachable only from the internal network.

Deploying to Kubernetes

Teams already using Kubernetes should use the official n8n Helm chart rather than writing manifests from scratch — the chart already handles Deployment, Service, Ingress, Secret, and persistence options:

Install n8n dengan Helm chart resmi
helm repo add n8n https://n8n-io.github.io/helm-charts
helm repo update
helm install n8n n8n/n8n --values values.yaml

Common values set in values.yaml: image and tag, postgresql.enabled with a password from Secret, persistence for n8n_data, extraEnv for N8N_ENCRYPTION_KEY and N8N_USER_MANAGEMENT_JWT_SECRET, as well as ingress.host for the domain. In a cluster, the biggest benefits are that failed nodes are restarted automatically and volumes are carried by PersistentVolumeClaims.

Queue Mode & Scaling Worker Nodes

When execution load starts to grow, executions don't always need to run in the main process. Queue mode moves executions to a Redis queue, and workers pull jobs from that queue. The main process still handles UI and API; workers consume the CPU for executions.

Enable it with EXECUTIONS_MODE=queue on all services, then run workers:

Menjalankan worker dengan concurrency tertentu
n8n worker --concurrency=10
Concurrency lewat environment variable
N8N_CONCURRENCY_PRODUCTION_LIMIT=10 n8n worker

Workers can run as separate containers with the same image — just the n8n worker command and the same env (EXECUTIONS_MODE=queue, QUEUE_BULL_REDIS_HOST, QUEUE_BULL_REDIS_PORT). Important note: concurrency below 5 can make the environment unstable, so start from that value upward. Don't forget to set N8N_COMMUNITY_NODES_ENABLED=true on both main and workers when using community nodes, because both sides must load the same nodes.

High Availability

Setting up redundancy means eliminating single points of failure:

  • Redis and PostgreSQL run with replication or as managed services, so one dead node doesn't cripple the queue and database.
  • Workers can be scaled horizontally by adding replicas — the Redis queue distributes jobs to whichever worker is idle.
  • Main can be multi-instance with N8N_MULTI_MAIN_SETUP_ENABLED=true, allowing several main replicas behind a load balancer. The QUEUE_HEALTH_CHECK_ACTIVE=true health check and the /healthz endpoint from episode 13 ensure only healthy replicas serve traffic.
  • Regular backups for the database (for example pg_dump) and the binary data volume; test restores periodically, because a backup never tested is the same as no backup.

Warning

Don't turn on queue mode and multi-main at the same time without a reason. Queue mode adds the Redis component; multi-main adds synchronization complexity. Add each layer only when the metrics in episode 13 genuinely show the need — avoid unused complexity.

Closing

Episode 15 moved n8n from experiment to production service. You chose a deployment strategy appropriate to context, built a Docker Compose stack with PostgreSQL and persistent volumes, secured access via a reverse proxy, managed deployments on Kubernetes with Helm, enabled queue mode for scaling workers, and set up high availability on top of Redis, PostgreSQL, and health checks.

Key takeaways:

  • Start with Docker Compose on a single VM, move to Kubernetes only when needed.
  • PostgreSQL is required for production, SQLite only for experiments; binary data can be moved to S3.
  • All state must be persistent — volumes, database, and .env that survive restarts.
  • Queue mode moves executions to workers via Redis, with a minimum concurrency of 5.
  • High availability means eliminating single points of failure and testing backups regularly.

In the next episode we dig into performance: Performance Optimization — breaking large workflows into sub-workflows, batch processing and concurrency, as well as optimizing execution runtime and throughput. See you there!