October 31, 2026 · 13 min read

n8n Self-Hosted: Docker or Kubernetes

Graphic title card for the article “n8n Self-Hosted: Docker or Kubernetes” with a stylised motif: three boxes connected by arrows, a small lightning bolt as the trigger at the start and a tick at the end.
Graphic: HumanITy

The fastest way to run n8n self-hosted is Docker: create a volume for the data, start the n8nio/n8n container on port 5678 and open http://localhost:5678 in your browser. For a server that runs permanently, use Docker Compose with Postgres, set your own N8N_ENCRYPTION_KEY and put a reverse proxy with TLS in front. Kubernetes pays off if you already run a cluster, using n8n's official Helm chart. And npm is on its way out: from n8n 3.0, the docs say, n8n ships only through Docker.

I ran n8n in my own Kubernetes cluster for a long time. I've been running Kubernetes clusters since 2017, today mostly k3s on Hetzner Cloud. I'm a CNCF Kubestronaut and wrote the Kubernetes Practical Guide published by Rheinwerk Computing. The commands and output below come from a test on 31 October 2026 on an Apple Silicon Mac with Docker Desktop 4.93.0, shortened.

Which route fits: Docker, Compose or Kubernetes

The docs are blunt: self-hosting takes server, container and security skills, mistakes can lead to data loss, security issues and downtime, and n8n recommends it for expert users. The three routes differ in how much operations work you take on:

Route Fits Database What you do yourself
docker run trying it out on your own machine SQLite in the volume little, but no TLS and no backup
Docker Compose one server running permanently Postgres recommended TLS, backups, updates
Kubernetes with Helm existing cluster, scaling with workers Postgres, plus Redis in queue mode all of that plus the cluster

There is also an install script, curl -fsSL https://get.n8n.io | sh. It writes compose.yml and .env and starts n8n with a sandbox for n8n Assistant and a web search service; the docs ask for at least 4 GB of RAM and 2 vCPUs. The docs page for a plain Docker install is now marked as outdated and points to Compose; for a test it still works.

Whether self-hosting is worth it at all is covered in n8n, Make, or Claude: What For?. What cloud and self-hosting cost is in n8n Pricing Explained: Cloud vs Self-Hosted.

Install n8n with Docker

You need Docker (Docker Desktop on Mac and Windows). Then two commands:

docker volume create n8n_data

docker run -d --name n8n \
  -p 5678:5678 \
  -e GENERIC_TIMEZONE="Europe/Berlin" \
  -e TZ="Europe/Berlin" \
  -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \
  -v n8n_data:/home/node/.n8n \
  n8nio/n8n:2.45.7

GENERIC_TIMEZONE sets the time zone for scheduled workflows, TZ the system time zone inside the container. The volume is mounted at /home/node/.n8n, where the database and the key live.

Pin the version instead of using latest. Per the docs, n8n ships a new minor version most weeks; stable is for production, beta is the newest and may be unstable. On test day, stable and latest on Docker Hub had the same digest as 2.45.7; the docs listed 2.46.3 as beta.

Two endpoints tell you whether n8n is running:

$ curl -s http://localhost:5678/healthz
{"status":"ok"}
$ curl -s http://localhost:5678/healthz/readiness
{"status":"error"}

In the test, /healthz said "ok" 2 seconds after the start, while /healthz/readiness still returned HTTP 503. n8n was setting up its database, and the log said: "Migrations in progress, please do NOT stop the process." On a fresh volume, readiness was "ok" after 4 seconds, and http://localhost:5678 served the editor. The first start including the image download took 81 seconds; locally the image takes 1.07 GB. Wait for readiness before stopping the container: in the test it was stopped once during this phase, and the next start reported "Last session crashed".

What lives in the volume: database and encryption key

On first start n8n logs: "No encryption key found - Auto-generating and saving to: /home/node/.n8n/config". In the test, that folder held the file config with the field encryptionKey (32 characters) and the SQLite database database.sqlite. n8n uses this key to encrypt every credential you store in workflows.

Next, the container was deleted and recreated with the same volume: same key, no new one generated. The flip side: docker volume rm n8n_data deletes workflows, credentials and key at once. Per the docs, a complete backup is this folder plus the database if you use Postgres.

n8n self-hosted with Docker Compose and Postgres

For a permanent server, the docs recommend Postgres over SQLite once more than a handful of users or workflows run around the clock. A lean compose.yml without the sandbox services, as tested, slightly shortened:

services:
  postgres:
    image: postgres:18
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
      PGDATA: /var/lib/postgresql/data
    volumes:
      - db-storage:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 5s
      retries: 10
  n8n:
    image: n8nio/n8n:${N8N_VERSION}
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
      DB_POSTGRESDB_USER: ${POSTGRES_USER}
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
      GENERIC_TIMEZONE: Europe/Berlin
      TZ: Europe/Berlin
    volumes:
      - n8n-data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy
volumes:
  db-storage:
  n8n-data:

The values go into a .env file that stays out of Git. Generate password and key with openssl rand -hex 16 and openssl rand -hex 32:

N8N_VERSION=2.45.7
POSTGRES_USER=n8n
POSTGRES_DB=n8n
POSTGRES_PASSWORD=<random value>
N8N_ENCRYPTION_KEY=<random value>

Keep the PGDATA line: per the n8n docs, Postgres 18 otherwise writes where the volume doesn't reach, and the database starts empty. Port 5678 is published on 127.0.0.1 only, so the way in from outside is the reverse proxy on the same server.

In the test, n8n was ready 4 seconds after docker compose up -d, running on Postgres 18.6. n8n had created 167 tables, and the key in config matched the one from .env.

Then the key check: n8n with a different N8N_ENCRYPTION_KEY on the same volume. The container got stuck in a restart loop:

Error: Mismatching encryption keys. The encryption key in the settings file
/home/node/.n8n/config does not match the N8N_ENCRYPTION_KEY env var.

n8n stores the key on first start and compares against it from then on. If the old key is lost, the docs say, credentials stored in Postgres can't be decrypted. Keep it in a password manager or secret store, not only on the server.

n8n on Kubernetes with the official Helm chart

For n8n on Kubernetes, n8n maintains an official Helm chart in the n8n-io/n8n-hosting repository, as an OCI package on ghcr.io. Per its README, a chart version ships every Wednesday with the current stable n8n. Chart 1.x installs n8n 2.x; chart 2.0 is a release candidate so far, which Helm skips without --devel. Two modes:

  • Queue mode (default): main pod, workers and optional webhook processors. Needs an external Postgres and Redis, which the chart does not bundle.
  • Standalone: one pod with SQLite on a PersistentVolumeClaim, meant for development and small-scale use according to the README.

In the test, standalone ran on minikube v1.39.0 with Kubernetes v1.37.0 and Helm v4.3.1:

kubectl create namespace n8n
kubectl create secret generic n8n-secrets -n n8n \
  --from-literal=N8N_ENCRYPTION_KEY=$(openssl rand -hex 32) \
  --from-literal=N8N_HOST=localhost \
  --from-literal=N8N_PORT=5678 \
  --from-literal=N8N_PROTOCOL=http
helm install n8n oci://ghcr.io/n8n-io/n8n-helm-chart/n8n \
  --version 1.18.0 -n n8n -f standalone.yaml
# standalone.yaml
queueMode:
  enabled: false
database:
  type: sqlite
  useExternal: false
redis:
  enabled: false
persistence:
  enabled: true
  size: 5Gi
secretRefs:
  existingSecret: "n8n-secrets"

Chart 1.18.0 shipped n8n 2.45.5, two patch versions behind that day's Docker image. The pod ran 12 seconds after helm install (image already on the node), and readiness said "ok" via kubectl port-forward. The chart uses the same endpoints as probes: /healthz for liveness and startup, /healthz/readiness for readiness. More in Liveness and Readiness Probes Explained.

One trap from the test: the first attempt used standalone.yaml from the main branch, where chart 2.0 is built. With chart 1.18.0 the pod stayed in CreateContainerConfigError: secret "n8n-db-password" not found. The example from release/n8n-2.x, shown above, adds useExternal: false and expects N8N_HOST, N8N_PORT and N8N_PROTOCOL in the Secret. Take examples from the branch matching your chart version.

Real operation adds Postgres, Redis, an Ingress with TLS and secrets from a secret store, such as the External Secrets Operator the README names. The basics are in Kubernetes Ingress and Ingress Controllers and Kubernetes Persistent Volumes and PVCs. Building a cluster just for n8n is a lot of operations for one application; how I run mine is in Kubernetes on Hetzner: k3s in Production.

Security: key, base URL, TLS and updates

Four points from the docs before n8n is reachable from the internet:

  1. Set and back up the encryption key yourself. Set N8N_ENCRYPTION_KEY before the first start; in queue mode every worker needs it.
  2. Set the base URL. Behind a reverse proxy, n8n otherwise builds the webhook URL wrongly from protocol, host and port. Set N8N_WEBHOOK_URL=https://n8n.example.com/ and N8N_PROXY_HOPS=1; the proxy passes on X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto. WEBHOOK_URL is deprecated since n8n 2.35.0. The editor address is set with N8N_EDITOR_BASE_URL.
  3. TLS through a reverse proxy. The docs recommend Traefik or a load balancer in front, which also renews certificates; their Hetzner guide uses Caddy. By default n8n sends cookies only over HTTPS (N8N_SECURE_COOKIE=true). Don't switch that off; put TLS in front instead. How to harden the server itself is in Securing Your Own Server: Linux and Windows.
  4. Update regularly. At least monthly per the docs, after checking the release notes for breaking changes and taking a full backup. With Compose: raise the version in .env, then docker compose pull, docker compose down and docker compose up -d.

After setup, build a first workflow with real inputs and error cases: Create an n8n Workflow: Tutorial with Examples.

Frequently asked questions

Can I install n8n without Docker?

Via npm for now, but per the docs not from n8n 3.0 on, which ships only through Docker. Existing npm installs keep working for now; new installs should use Docker.

Do I need Postgres?

Not for testing; SQLite in the volume is enough. For a server with several users or round-the-clock workflows the docs recommend it, and queue mode requires it.

What happens if I lose the encryption key?

Stored credentials can no longer be decrypted. With a different key on the old volume, n8n doesn't start at all; in the test it failed with "Mismatching encryption keys".

Docker or Kubernetes: which one do I pick?

Docker Compose for a single server. Kubernetes if you already run a cluster or want to scale workers. The Helm chart takes the manifests off your hands, not the operations.

Where to go next

Once n8n runs, the real work begins: workflows and agents. How an AI agent is built in n8n and what I took away is in n8n AI Agent: What I Learned Building One. More ways to build your own agent are on the AI Agents page and in Build an AI Agent for Free: What Works?.

For n8n in a cluster, all the basics are on the Kubernetes page. And if you get stuck during the install: bring the error message to the community, and we look at it together.

Kevin Welter

Kevin Welter

Developer, IT architect, author of technical books (Kubernetes, cloud infrastructures) and speaker. Runs his business with an AI workforce of fourteen AI employees and shows solo business owners in his community how to hire their first AI employee.

More about AI employees

Your first AI employee up and running within an hour

Join the community

$5/month founding price, first 50 members