(302) 414-9101
1001 S Main St, STE 600, Kalispell, MT 59901
contact@zarghamlabs.com

Blog Details

How to Self-Host n8n on a VPS: Complete Setup Guide for 2026

Why Self-Host n8n?

n8n Cloud costs $20–120/month depending on executions. A self-hosted n8n instance on a $6–10/month VPS (Hetzner CX22 or DigitalOcean Droplet) runs unlimited workflows, unlimited executions, and stores all your data privately — for roughly the cost of a cup of coffee per month. For SaaS founders, agencies, and developers running serious automation workloads, self-hosting is almost always the right economic decision.

This guide gets you from a blank VPS to a production-ready n8n instance with SSL, a domain, basic authentication, and automatic restarts — in under an hour.

What You Need

  • A VPS with at least 2GB RAM (Hetzner CX22 at €3.79/month, DigitalOcean $6/month Basic Droplet, or Vultr $6/month)
  • Ubuntu 22.04 LTS (recommended)
  • A domain name (or subdomain) pointed to your server’s IP — e.g., n8n.yourdomain.com
  • Basic SSH access comfort

Step 1 — Server Initial Setup

SSH into your fresh server and run initial updates:

ssh root@YOUR_SERVER_IP
apt update && apt upgrade -y
apt install -y curl git ufw

Set up a firewall — allow SSH, HTTP, and HTTPS only:

ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Step 2 — Install Docker and Docker Compose

curl -fsSL https://get.docker.com | sh
apt install -y docker-compose-plugin
systemctl enable docker
systemctl start docker

Verify both are working:

docker --version
docker compose version

Step 3 — Point Your Domain to the Server

In your domain registrar’s DNS settings, add an A record:

  • Type: A
  • Name: n8n (or @ for root domain)
  • Value: YOUR_SERVER_IP
  • TTL: 300

Wait 5–15 minutes for DNS propagation. You can check with: nslookup n8n.yourdomain.com

Step 4 — Create the Docker Compose File

Create a directory and the compose file:

mkdir /opt/n8n && cd /opt/n8n
nano docker-compose.yml

Paste this configuration (replace the values in caps):

version: "3.8"

services:
  traefik:
    image: traefik:v3.0
    restart: always
    command:
      - "--api=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=YOUR_EMAIL"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt

  n8n:
    image: n8nio/n8n:latest
    restart: always
    environment:
      - N8N_HOST=YOUR_DOMAIN (e.g. n8n.yourdomain.com)
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://YOUR_DOMAIN/
      - GENERIC_TIMEZONE=Asia/Karachi
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=YOUR_USERNAME
      - N8N_BASIC_AUTH_PASSWORD=YOUR_STRONG_PASSWORD
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8nuser
      - DB_POSTGRESDB_PASSWORD=YOUR_DB_PASSWORD
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - postgres
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.n8n.rule=Host(`YOUR_DOMAIN`)"
      - "traefik.http.routers.n8n.entrypoints=websecure"
      - "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
      - "traefik.http.services.n8n.loadbalancer.server.port=5678"

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

volumes:
  n8n_data:
  postgres_data:
  letsencrypt:

Step 5 — Launch n8n

cd /opt/n8n
docker compose up -d

Watch the logs to confirm everything started cleanly:

docker compose logs -f n8n

Within 1–2 minutes, Traefik will obtain a Let’s Encrypt SSL certificate and n8n will be live at https://YOUR_DOMAIN. Login with the basic auth credentials you set.

Step 6 — Production Hardening

Automatic Backups

Your workflows are stored in PostgreSQL. Back up the volume daily with a cron job:

crontab -e
# Add this line:
0 2 * * * docker exec n8n-postgres-1 pg_dump -U n8nuser n8n > /opt/n8n/backups/n8n_$(date +\%Y\%m\%d).sql

Keeping n8n Updated

cd /opt/n8n
docker compose pull
docker compose up -d

Monitoring

Set up a free uptime monitor (UptimeRobot or BetterUptime) pointing to your n8n URL. You’ll get notified if the server goes down. For a $6/month VPS this is sufficient — uptime on Hetzner and DigitalOcean is consistently 99.9%+.

Common Issues and Fixes

  • SSL certificate not issuing: DNS hasn’t propagated yet. Wait 15 minutes and run docker compose restart traefik
  • n8n won’t start: Check logs with docker compose logs n8n. Common cause: wrong DB password or port conflict
  • Webhooks not working: Ensure WEBHOOK_URL is set correctly to https://YOUR_DOMAIN/ with the trailing slash
  • Running out of memory: Enable swap: fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile

n8n Automation Services by Zargham Labs | Hire a SaaS Developer

Leave A Comment