Hosto

Sign In

How to Self-Host n8n in 2026: Docker, VPS, or Managed (a Practical Guide)

By Tushar Khatri

Monitor showing syntax-highlighted source code with line numbers

If you've been paying per-execution for workflow automation, learning how to self-host n8n is one of the highest-leverage moves you can make. n8n is fair-code licensed, which means you can run it on your own server for free with unlimited executions: no per-run metering, no workflow caps. You also keep your data on infrastructure you control, which matters a lot when your workflows touch customer records, API keys, or internal databases. The trade-off is that a self-hosted n8n instance becomes your responsibility: TLS, backups, updates, and the occasional 2 a.m. "why is my webhook not firing" session.

This guide walks through three practical paths: a straightforward Docker setup on a cheap VPS, a docker-compose stack with Postgres for heavier workloads, and managed n8n hosting for people who want the self-hosted benefits without the ops work. All commands are current for 2026 and tested against n8n's official Docker image.

Why Self-Host n8n at All?

Three reasons come up again and again:

  1. Cost. n8n's fair-code license lets you self-host with unlimited executions. A small VPS from Hetzner runs about €4–5/month; DigitalOcean starts at $6/month. If you run thousands of executions a day, self-hosting is dramatically cheaper than any per-execution pricing model. We've broken down the exact math in our n8n Cloud vs self-hosted cost comparison.
  2. Privacy and control. Your workflow data, credentials, and execution logs live on your server. For teams handling sensitive data, this is often a compliance requirement, not a preference.
  3. No artificial limits. Unlimited workflows, unlimited executions, every node available. The software is the same. You're just running it yourself.

The honest counterpoint: self-hosting costs time. Budget for initial setup plus ongoing maintenance. If that trade doesn't work for you, skip to option 3.

Option 1: How to Self-Host n8n with Docker on a VPS

This is the setup most people should start with. You'll need a VPS, a domain (or subdomain) pointed at it, and about 30–45 minutes.

Step 1: Provision a VPS

Pick any provider: Hetzner (~€4–5/mo) and DigitalOcean ($6/mo) are popular choices. For real workloads, 2 GB of RAM is the recommended minimum; n8n itself is lightweight, but workflows that process large payloads or run many executions in parallel will eat memory quickly. Choose Ubuntu 24.04 LTS or Debian 12 as the OS.

Once the server is up, SSH in and do the basics:

ssh root@your-server-ip
apt update && apt upgrade -y

Point a DNS A record at the server now (for example n8n.yourdomain.com → your server IP) so it has time to propagate while you work.

Step 2: Install Docker

Use the official convenience script:

curl -fsSL https://get.docker.com | sh

Verify it works:

docker --version

Step 3: Run n8n

The official quickstart command gets n8n running immediately:

docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

This maps port 5678 and mounts a named volume at /home/node/.n8n, the directory where n8n stores its SQLite database, credentials, and encryption key. The volume is not optional. Without it, every container restart wipes your workflows.

The quickstart is great for a first look, but for a real deployment you want the container detached, restarting automatically, and configured with production environment variables:

docker run -d --restart unless-stopped --name n8n \
  -p 5678:5678 \
  -e N8N_HOST="n8n.yourdomain.com" \
  -e N8N_PROTOCOL="https" \
  -e N8N_PORT="5678" \
  -e WEBHOOK_URL="https://n8n.yourdomain.com/" \
  -e GENERIC_TIMEZONE="America/New_York" \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n

Two of these deserve special attention:

  • WEBHOOK_URL must be your public HTTPS URL. n8n uses it to register webhook endpoints with external services. If it's wrong or missing, webhooks register with the container's internal address and silently never fire. This is the single most common self-hosting mistake; see our n8n webhook troubleshooting guide if you hit it.
  • GENERIC_TIMEZONE controls when your Cron and Schedule nodes fire. Set it to your actual timezone or your "daily 9 a.m. report" will arrive at UTC 9 a.m.

Step 4: Reverse Proxy and TLS with Caddy

Never expose n8n directly over plain HTTP. Your credentials and webhook payloads would travel unencrypted. A reverse proxy terminates TLS in front of the container, and Caddy is the least-effort way to do it because it provisions and renews Let's Encrypt certificates automatically.

Install Caddy (Debian/Ubuntu):

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

Then the entire Caddy config is two lines. Edit /etc/caddy/Caddyfile:

n8n.yourdomain.com {
    reverse_proxy localhost:5678
}

Reload Caddy:

systemctl reload caddy

That's it. Caddy fetches a certificate, and https://n8n.yourdomain.com now serves your instance. Open it in a browser and create your owner account. If you prefer nginx, the equivalent is a standard proxy_pass to localhost:5678 plus certbot for certificates: more steps, same result.

Step 5: Protect the Encryption Key and Set Up Backups

On first startup, n8n generates an encryption key and stores it in /home/node/.n8n/config inside the volume. Every credential you save in n8n is encrypted with this key. If you lose the key, your stored credentials are unrecoverable: the database will still hold them, but n8n can't decrypt them, and you'll be re-entering every API token by hand.

You can also set it explicitly, which makes disaster recovery much easier:

-e N8N_ENCRYPTION_KEY="your-long-random-string"

Generate one with openssl rand -hex 32, store it in your password manager, and pass it as an env var from day one.

For backups, archive the data volume regularly:

docker run --rm -v n8n_data:/data -v /root/backups:/backup alpine \
  tar czf /backup/n8n-backup-$(date +%F).tar.gz -C /data .

Drop that in a cron job, and copy the archives off the server (rsync to another machine, or push to object storage). A backup that lives only on the server it's backing up isn't a backup.

Option 2: docker-compose with Postgres

n8n defaults to SQLite, and for a personal instance with modest traffic, SQLite is genuinely fine. But for heavier use (many concurrent executions, large execution histories, team usage), Postgres is the recommended database. Switching is just a matter of setting DB_TYPE=postgresdb plus connection env vars, and docker-compose makes the whole stack declarative:

services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: change-me
      POSTGRES_DB: n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_PORT: 5432
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: change-me
      N8N_HOST: n8n.yourdomain.com
      N8N_PROTOCOL: https
      WEBHOOK_URL: https://n8n.yourdomain.com/
      N8N_ENCRYPTION_KEY: your-long-random-string
      GENERIC_TIMEZONE: America/New_York
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - postgres

volumes:
  postgres_data:
  n8n_data:

Bring it up with:

docker compose up -d

The Caddy setup from option 1 is identical. Your backup routine now needs to cover both the n8n volume and a Postgres dump (docker compose exec postgres pg_dump -U n8n n8n > n8n-db.sql).

Option 3: Managed n8n Hosting

Here's the honest assessment: everything above is very doable, but it never stops being your job. Updates, certificate renewals, backup verification, disk monitoring, the occasional migration when a major n8n version changes something. Each item is small, but they add up, and the failure mode of skipping them is losing workflows or credentials.

Managed n8n hosting splits the difference between DIY and n8n Cloud: you get a self-hosted-style instance (your own server, unlimited executions), but someone else carries the pager. Hosto's managed n8n runs a dedicated KVM VM per customer (not a shared multi-tenant container), starting at $12/month, or $9/month billed annually, for 1 vCPU, 2 GB RAM, and 40 GB NVMe storage, with larger tiers at $24 and $39. That includes unlimited executions, free SSL, a custom domain, daily backups, automatic updates, and the same price at renewal.

The math is simple. A DIY VPS costs $5–6/month plus your time; managed costs a few dollars more and your time drops to roughly zero. If you enjoy running servers or you're on a strict budget, DIY wins. If your workflows are business-critical and your hours are worth more than the difference, managed wins. There's no universally right answer. Just be honest about which failure you'd rather own.

Maintenance Checklist for Self-Hosted n8n

Whichever DIY route you pick, put these on a recurring schedule:

  • Updates. n8n releases frequently. Pin a specific image version (e.g. docker.n8n.io/n8nio/n8n:1.x.y) rather than latest, read the release notes, and test upgrades before applying them to the instance running production workflows. To upgrade: pull the new image, stop and remove the old container, start a new one with the same volume and env vars.
  • Backups. Automate volume archives (and Postgres dumps if applicable), store them off-server, and, critically, test a restore at least once. An untested backup is a hypothesis.
  • Encryption key. Confirm N8N_ENCRYPTION_KEY is recorded somewhere outside the server. This is the one item on this list with no recovery path.
  • Monitoring. At minimum, an uptime check against your instance URL and a disk-space alert. Execution history grows; a full disk is the most common way a quietly healthy instance falls over.
  • Security. Keep the OS patched (apt upgrade), use SSH keys instead of passwords, and let the reverse proxy be the only thing exposed on ports 80/443.

FAQ

Is self-hosted n8n really free?

The software is, yes. n8n is fair-code licensed, and self-hosting includes unlimited workflows and executions at no cost. Your real expenses are the server (from roughly €4–6/month on Hetzner or DigitalOcean) and your time for setup and maintenance.

How much RAM does n8n need?

For real workloads, 2 GB is the recommended minimum. n8n will start on less, but workflows that handle large payloads or run concurrently will hit memory pressure fast, and an OOM-killed container mid-execution is painful to debug.

Do I need Postgres, or is SQLite enough?

SQLite is the default and works fine for small setups: a personal instance with a handful of workflows. Move to Postgres (DB_TYPE=postgresdb with the connection env vars shown above) when you have heavier execution volume, multiple users, or you simply want easier, more robust backups via pg_dump.

Why aren't my webhooks working on self-hosted n8n?

Almost always WEBHOOK_URL. It must be set to your public HTTPS URL so n8n registers webhooks at an address the outside world can reach. Otherwise external services get an internal address and calls never arrive. Check that variable first, then work through our n8n webhook troubleshooting guide for reverse-proxy and firewall issues.

Host it without the ops.

WordPress, WooCommerce, static sites, and dedicated n8n VMs. Same price at renewal, live in minutes.