tutorial

How to Self-Host n8n on a VPS in 2026

Step-by-step tutorial for self-hosting n8n on a small Linux VPS using Docker Compose, a persistent volume, HTTPS via Caddy with automatic Let's Encrypt certificates, and basic auth on the editor. Tested on a Hetzner CX22 at €4.51/month as of April 2026.

Overview

n8n is an open-source workflow automation platform that can be self-hosted on a small Linux VPS. As of April 2026 the official Docker image runs on as little as 1 GB of RAM, which puts a single-user n8n instance well within the budget of a $5/month VPS plan from providers such as Hetzner, DigitalOcean, or Vultr. This tutorial walks through a production-leaning setup using Docker Compose, a persistent volume, HTTPS via Caddy with automatic Let's Encrypt certificates, and basic auth on the editor.

The result is an n8n instance reachable at https://n8n.example.com that survives reboots, retains workflow data, and rejects unauthenticated access at the proxy layer. The same recipe scales up by changing the VPS plan; the compose file does not change.

Prerequisites

  • A Linux VPS with at least 1 GB RAM and 1 vCPU (a Hetzner CX22 at €4.51/month works as of April 2026)
  • Docker Engine 24.0 or newer and Docker Compose v2
  • A domain name with an A record pointing at the VPS IPv4 address
  • Inbound firewall rules permitting TCP ports 80 and 443

Step 1: Prepare the Server

Create a working directory and a non-root user (skip the user step if already done as part of the VPS hardening pass):

sudo mkdir -p /opt/n8n/{data,caddy-data,caddy-config}
sudo chown -R $USER:$USER /opt/n8n
cd /opt/n8n

Step 2: Generate Secrets

n8n encrypts stored credentials with N8N_ENCRYPTION_KEY. Losing this key after credentials are stored makes them unrecoverable, so back it up off-server.

cat > .env <<EOF
N8N_HOST=n8n.example.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.example.com/
GENERIC_TIMEZONE=Europe/London
N8N_ENCRYPTION_KEY=$(openssl rand -hex 16)
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=$(openssl rand -hex 16)
EOF
chmod 600 .env

Step 3: Write docker-compose.yml

services:
  n8n:
    image: n8nio/n8n:1.85
    restart: unless-stopped
    environment:
      - N8N_HOST
      - N8N_PROTOCOL
      - WEBHOOK_URL
      - GENERIC_TIMEZONE
      - N8N_ENCRYPTION_KEY
      - N8N_BASIC_AUTH_ACTIVE
      - N8N_BASIC_AUTH_USER
      - N8N_BASIC_AUTH_PASSWORD
    volumes:
      - ./data:/home/node/.n8n
    expose:
      - "5678"

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./caddy-data:/data
      - ./caddy-config:/config
    depends_on:
      - n8n

Pin the n8n image to a specific minor tag rather than latest. Minor releases occasionally include database migrations that require a clean shutdown.

Step 4: Configure Caddy for HTTPS

Caddy automatically requests and renews Let's Encrypt certificates as long as DNS resolves and ports 80 and 443 are reachable.

n8n.example.com {
    reverse_proxy n8n:5678
    encode gzip
}

Save as /opt/n8n/Caddyfile.

Step 5: First Boot

docker compose up -d
docker compose logs -f n8n

Wait until the log shows Editor is now accessible via:. Open https://n8n.example.com in a browser. The basic-auth prompt accepts the credentials from .env. After signing in, n8n displays its own owner-setup form; complete it to register the first internal user.

Step 6: Backups

Workflow data lives entirely in /opt/n8n/data. A nightly backup is enough for low-volume single-user use:

0 3 * * * tar czf /var/backups/n8n-$(date +\%F).tgz /opt/n8n/data /opt/n8n/.env

Copy the resulting archives off-server (rsync, S3, restic). The .env file is included because the encryption key is required to decrypt restored credentials.

Step 7: Updating n8n

To upgrade, edit the image tag in docker-compose.yml, then:

docker compose pull
docker compose up -d
docker compose logs -f n8n

Read the n8n changelog before crossing minor versions. Major version bumps (for example 1.x to 2.x) sometimes require a manual migration step.

Common Issues

  • Caddy fails to issue a certificate. DNS has not propagated, or port 80 is blocked. Run dig n8n.example.com and curl http://n8n.example.com from another machine.
  • Editor is now accessible via http://localhost:5678 and webhooks fail. WEBHOOK_URL is missing. Set it to the public HTTPS URL.
  • Workflows pause after restart. Free RAM is exhausted. Upgrade the VPS to 2 GB or move to queue mode with PostgreSQL and Redis.

For higher-throughput deployments, see the related guide on running n8n with a dedicated PostgreSQL backend and the n8n vs ActivePieces comparison for context on alternative open-source platforms. The n8n tool page tracks current pricing for the n8n Cloud option.

Editor's Note: We run roughly a dozen client n8n instances on Hetzner CX22 boxes (€4.51/month, 4 GB RAM as of April 2026) and have not yet hit a CPU ceiling on single-tenant workloads under 5,000 executions per day. The single most expensive mistake we made early on was not backing up the encryption key off-server; one VPS rebuild silently invalidated about 40 stored credentials. Backups now include .env by default.

Last updated: | By Rafal Fila

Tools Mentioned

Related Guides

Related Rankings

Common Questions