We've self-hosted n8n for dozens of clients over the past two years. The cloud version is fine for testing, but once you hit real workflow volume, the pricing jumps fast. Self-hosting puts you in control: unlimited workflows, unlimited executions, your data stays on your server.

This guide covers the exact setup we use in production — not a "hello world" demo. By the end you'll have n8n running on a proper domain with HTTPS, auto-restart, and basic security hardening.

What You Need Before You Start

We typically use Hetzner CX21 (2 vCPU, 4GB RAM, €4.15/mo) for client setups — it's overkill for most workflows but leaves headroom for growth. DigitalOcean's $6/mo droplet works fine for lighter use.

Step 1: Install Docker & Docker Compose

SSH into your server and run the following:

# Update system
sudo apt update && sudo apt upgrade -y

# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker

# Verify
docker --version

Docker Compose V2 is included with modern Docker installs. Confirm with docker compose version. If it's missing, install the plugin: sudo apt install docker-compose-plugin

Step 2: Point Your Domain to the Server

Before setting up HTTPS, your domain needs to resolve to your server IP. In your DNS provider, add an A record:

Type: A
Name: n8n (or @ for root domain)
Value: YOUR.SERVER.IP.ADDRESS
TTL: 300

Wait 5–10 minutes for DNS to propagate. Confirm with dig n8n.yourdomain.com +short — it should return your server IP.

Step 3: Create the Docker Compose File

Create a directory for your n8n stack and add the compose file:

mkdir ~/n8n-stack && cd ~/n8n-stack
nano docker-compose.yml

Paste this configuration:

version: '3.8'

services:
  n8n:
    image: n8nio/n8n:latest
    restart: always
    ports:
      - "5678:5678"
    environment:
      - N8N_HOST=n8n.yourdomain.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      - GENERIC_TIMEZONE=Europe/London
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=changeme123
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:

Important: Change N8N_BASIC_AUTH_PASSWORD to something secure. This is your login password.

Step 4: Set Up Caddy for HTTPS

Caddy handles HTTPS automatically — no certbot needed. Install it:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Create your Caddyfile:

sudo nano /etc/caddy/Caddyfile
n8n.yourdomain.com {
  reverse_proxy localhost:5678
}

That's it. Caddy fetches a Let's Encrypt certificate automatically on first request. Reload Caddy:

sudo systemctl reload caddy

Step 5: Start n8n

cd ~/n8n-stack
docker compose up -d

Wait 30 seconds, then visit https://n8n.yourdomain.com. You should see the n8n login screen. Log in with the credentials you set above.

✓ What You Have Now

Production Hardening (Don't Skip This)

The basic setup above works, but for anything you're relying on daily, add these:

Adding a PostgreSQL Database

By default n8n uses SQLite — fine for small use, but under heavy load (1000+ executions/day) you'll want PostgreSQL. Update your compose file:

services:
  postgres:
    image: postgres:15
    restart: always
    environment:
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=strongpassword
      - POSTGRES_DB=n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data

  n8n:
    # ... existing config ...
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=strongpassword
      - DB_POSTGRESDB_DATABASE=n8n
    depends_on:
      - postgres

Updating n8n

Keeping n8n updated is two commands:

cd ~/n8n-stack
docker compose pull
docker compose up -d

The old container is replaced with zero workflow data loss (it's stored in the volume). We run updates on client servers monthly — check the n8n changelog first for breaking changes.

Monitoring & Alerts

n8n has built-in workflow execution logging under Settings → Log Streaming. For server-level monitoring, we use Uptime Kuma — another Docker container that pings your n8n URL and sends Telegram/Slack alerts if it goes down. Free, self-hosted, five minutes to set up.

docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data louislam/uptime-kuma:1

Cost Comparison: Self-Host vs Cloud

Option Monthly Cost Executions Workflows
n8n Cloud Starter $24 2,500/mo 5 active
n8n Cloud Pro $60 10,000/mo 15 active
Self-hosted (Hetzner CX21) ~$5 Unlimited Unlimited

For our agency clients, self-hosting typically pays for itself within the first month. The only real cost is your time setting it up — which is why this guide exists.

When to Use Cloud Instead

Self-hosting isn't always the right call. Stick with n8n cloud if:

⚡ TL;DR Setup Checklist

Questions or hitting a wall? We help teams migrate from n8n cloud to self-hosted as part of our automation builds. Get in touch if you want it done for you.