Skip to main content
All postsAutomation Tools

Self-Hosting n8n: A Complete 2026 Setup Guide

Step-by-step guide to deploying n8n on your own infrastructure in 2026 — Docker, environment configuration, SSL, backups, and what to watch for in production.

Ross Devins
May 3, 2026 14 min read
Part of the guide:Ops Automation: What It Is and How B2B Teams Do It
Cover image for Self-Hosting n8n: A Complete 2026 Setup Guide

Self-hosting n8n gets you full control over your data, predictable costs, and unlimited workflow executions — all for the price of a $15/month VPS. The tradeoff: you're responsible for setup, updates, and keeping the lights on.

This guide walks through a production-ready n8n deployment in about 90 minutes. We use this exact stack for the n8n instances we host on behalf of clients.

What self-hosting actually saves

The honest comparison, at three realistic volume tiers:

Monthly executions Self-hosted n8n n8n Cloud Zapier equivalent
~2,500 $12–$15 (VPS) $20–$24 $73–$103
~25,000 $15–$25 (VPS) $60–$120 $400–$800
~100,000+ $25–$50 (bigger VPS) Enterprise pricing $1,000–$1,800+

The self-hosted column is flat because you're paying for a server, not per execution. That's the whole economic argument: your automation bill stops scaling with your automation ambition. Add roughly 2 to 4 hours a month of maintenance attention (updates, backup checks), which is the real cost most comparisons leave out. If those hours are worth more than the savings, skip to when self-hosting isn't worth it.

For the full platform decision (including Make, and when Zapier is still the right answer), see our n8n vs Zapier vs Make comparison.

Prerequisites

  • A domain name (or subdomain) you control. We'll use n8n.yourcompany.com for examples.
  • A VPS with at least 2 GB RAM and 1 vCPU. DigitalOcean's $12/month Basic droplet is the sweet spot for most teams. AWS Lightsail and Hetzner CX22 work equally well.
  • SSH access to that VPS, with a non-root sudo user configured.
  • About 90 minutes.

Sizing, if you want to plan ahead rather than resize later:

Workload RAM / vCPU Example
Up to ~10k executions/mo, simple workflows 2 GB / 1 vCPU DO Basic $12, Hetzner CX22
10k–50k/mo, or AI nodes in the mix 4 GB / 2 vCPU DO $24, Hetzner CX32
50k+/mo, heavy payloads, many webhooks 8 GB / 4 vCPU + queue mode DO $48, Hetzner CX42

Memory is the constraint that bites first, not CPU. Workflows that loop over large arrays or process file attachments hold their whole payload in RAM per execution.

Step 1: Server setup (10 min)

SSH into your VPS and install Docker. On Ubuntu 24.04:

sudo apt update && sudo apt upgrade -y
sudo apt install -y docker.io docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
exit  # log out and back in for the group change to apply

Confirm Docker works:

docker run --rm hello-world

Step 2: DNS pointing (5 min)

In your DNS provider, add an A record pointing n8n.yourcompany.com to your VPS's public IP address. Let it propagate (usually 1–5 minutes).

Step 3: Docker Compose configuration (15 min)

Create a working directory and a docker-compose.yml file:

mkdir -p ~/n8n && cd ~/n8n

Paste this into ~/n8n/docker-compose.yml:

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data

  n8n:
    image: n8nio/n8n:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_HOST: ${N8N_HOST}
      N8N_PORT: 5678
      N8N_PROTOCOL: https
      WEBHOOK_URL: https://${N8N_HOST}/
      GENERIC_TIMEZONE: America/New_York
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
    depends_on:
      - postgres
    volumes:
      - n8n_data:/home/node/.n8n

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config

volumes:
  postgres_data:
  n8n_data:
  caddy_data:
  caddy_config:

Create a .env file in the same directory:

cat > .env <<EOF
POSTGRES_PASSWORD=$(openssl rand -hex 24)
N8N_HOST=n8n.yourcompany.com
N8N_ENCRYPTION_KEY=$(openssl rand -hex 24)
EOF
chmod 600 .env

The encryption key is critical — it encrypts your credentials inside n8n. Back this up somewhere secure. If you lose it, every saved credential becomes unreadable.

Step 4: Caddy reverse proxy + auto-SSL (5 min)

Create ~/n8n/Caddyfile:

n8n.yourcompany.com {
    reverse_proxy n8n:5678
}

Caddy will automatically obtain a Let's Encrypt SSL certificate when it starts.

Step 5: Start everything (5 min)

cd ~/n8n
docker compose up -d
docker compose logs -f n8n

You should see n8n start. Once you see Editor is now accessible via: https://n8n.yourcompany.com/, hit Ctrl+C to exit the logs (the container keeps running).

Visit https://n8n.yourcompany.com in your browser. You'll be prompted to create the owner account. Use a real email — n8n uses it for password recovery, and there's no built-in "I forgot my admin password" flow for self-hosted instances.

Step 6: Production hardening

A working n8n instance isn't a production-ready n8n instance. Don't skip these.

Backup the database nightly. Add this to root's crontab:

0 2 * * * cd /home/youruser/n8n && docker compose exec -T postgres pg_dump -U n8n n8n | gzip > /home/youruser/backups/n8n-$(date +\%Y\%m\%d).sql.gz

Rotate to keep the last 30 days. Push to S3 / Backblaze B2 weekly for off-site copies.

Enable basic auth on the editor (separate from the n8n user account):

# Add to the n8n service environment block:
N8N_BASIC_AUTH_ACTIVE: "true"
N8N_BASIC_AUTH_USER: ${BASIC_AUTH_USER}
N8N_BASIC_AUTH_PASSWORD: ${BASIC_AUTH_PASSWORD}

Monitor uptime. Free options: UptimeRobot, BetterStack, or self-hosted Uptime Kuma. Check https://n8n.yourcompany.com/healthz every 60 seconds.

Subscribe to n8n's security advisories at https://github.com/n8n-io/n8n/security/advisories. Critical patches roll out monthly.

Lock down the server itself. The n8n container is only as safe as the box it runs on:

# Firewall: only SSH, HTTP, HTTPS
sudo ufw allow OpenSSH && sudo ufw allow 80 && sudo ufw allow 443
sudo ufw enable

# Keys only, no password SSH
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart ssh

# Ban brute-force attempts
sudo apt install -y fail2ban && sudo systemctl enable --now fail2ban

Also turn on two-factor auth inside n8n itself (Settings → Personal → Two-factor authentication) for the owner account. Anyone who gets into that account gets every credential your workflows use.

Scaling past a single box

A single instance handles more than most teams expect, but two symptoms tell you it's time for queue mode: webhook responses slowing down under load, or long-running workflows blocking short ones.

Queue mode splits n8n into a main process (handles the UI and webhooks) and worker processes that execute workflows, coordinated through Redis. The compose changes are modest: add a redis service, set EXECUTIONS_MODE: queue and the QUEUE_BULL_REDIS_HOST variables on the main service, and run one or more copies of the image with command: worker. Each worker scales execution capacity roughly linearly, and workers can live on separate machines when one box stops being enough.

Don't start in queue mode. Start simple, and let real load justify the complexity. Most of our client instances never need it.

Self-hosting for regulated teams

Self-hosted n8n is what makes automation viable at all for HIPAA, PCI, and strict-SOC 2 environments, because the workflow engine (and every byte of sensitive data it touches) stays inside your own infrastructure. No BAA negotiation with an automation vendor, because there's no vendor in the data path.

The additions that make an instance compliance-ready on top of this guide: run it inside the same cloud account as your other regulated systems, pull credentials from your secrets manager rather than pasting them into the UI, ship the Postgres execution logs to your SIEM or log archive for audit trail, and set EXECUTIONS_DATA_PRUNE retention to match your data-retention policy. That configuration is the backbone of every build in our healthcare automation practice, and the same pattern clears review for financial services clients.

Updates

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

Major-version upgrades occasionally have breaking changes. Always read the release notes first, and test in staging before pulling into production.

The four problems everyone hits

Webhooks don't fire. Ninety percent of the time it's WEBHOOK_URL missing or wrong in the environment. n8n generates webhook URLs from that variable, so if it's unset the URLs point at localhost and nothing external can reach them. Set it to your full public HTTPS URL and restart.

Executions die on large data. An out-of-memory kill mid-execution usually means a workflow is looping over a big array or handling file payloads. Short term: bump the VPS RAM. Long term: batch the loop with SplitInBatches so the whole dataset never sits in memory at once.

The encryption key gets lost. There's no recovery. Every stored credential is unreadable and has to be re-entered by hand. This is why Step 3 says to back up N8N_ENCRYPTION_KEY somewhere that isn't the server itself. Do it now if you skipped it.

An upgrade won't start. Database migrations occasionally fail on version jumps that skip several releases. Restore last night's backup, then step through intermediate versions instead of jumping straight to latest. (You have last night's backup, because you set up the cron job in Step 6.)

When self-hosting isn't worth it

A few cases where n8n Cloud is the better call:

  • You don't have anyone comfortable with Docker or Linux.
  • Your workflow volume is genuinely low (< 1,000 executions/month).
  • Compliance requirements specifically want a managed/SOC2 environment.

For everyone else: self-hosting routinely saves $200–$500/month versus n8n Cloud or Zapier equivalents.

What we do for clients

We host and manage production n8n instances for B2B operations teams who want self-hosted control but don't want to babysit the infrastructure. Everything in this guide, plus monitoring, backup verification, monthly patching, and 24-hour incident response — the full menu is on our n8n consulting and development page.

If that's interesting, book a 30-minute discovery call and we'll talk through your stack. Or browse the automation catalogue for the workflows we ship most often.

Related reading: What is n8n? · n8n vs Zapier vs Make · Migrating from Zapier to n8n · The ops automation guide

Want us to automate this for you?

Book a 30-minute discovery call — no pressure, no commitment.