n8n is the kind of tool you start using lightly and then quietly route half your operations through. At which point "it's running on someone's cloud seat, metered per execution, with my API keys living on their servers" starts to feel less great. Self-hosting fixes all three — flat cost, no execution cap, and your keys stay on a box you own. With Docker it's a fifteen-minute job.
How much server it actually needs
Honest numbers first, so you don't over- or under-buy:
- ~2 GB RAM is the sweet spot — n8n plus its Postgres database plus normal workflows sit comfortably here.
- 1 GB works if your workflows are light, but you'll notice it on bigger runs.
- 4 GB if you do heavy parallel executions or push large payloads through.
n8n isn't CPU-hungry at rest; it spikes during runs. A 2-core box is fine for most setups. (More on matching specs to workload in the sizing guide.)
The Docker setup
On a fresh Ubuntu/Debian box, install Docker:
curl -fsSL https://get.docker.com | sudo sh
Make a folder and a docker-compose.yml — n8n with a persistent volume and Postgres:
services:
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=n8n.yourdomain.com
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.yourdomain.com/
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=db
- DB_POSTGRESDB_PASSWORD=change-me
volumes:
- ./n8n-data:/home/node/.n8n
depends_on: [db]
db:
image: postgres:16
restart: always
environment:
- POSTGRES_PASSWORD=change-me
- POSTGRES_DB=n8n
volumes:
- ./db-data:/var/lib/postgresql/data
sudo docker compose up -d
Two things worth pointing out: the volumes (n8n-data, db-data) are what keep your workflows alive across restarts and upgrades — don't skip them. And n8n is bound to 127.0.0.1, not 0.0.0.0 — it's not exposed to the internet directly. That's deliberate; the next step handles access safely.
Access: HTTPS or a tunnel
- Public URL (needed for OAuth nodes and webhooks): point a subdomain at the server and run a reverse proxy (Caddy is the least-effort — automatic HTTPS) in front of
127.0.0.1:5678. This is where a dedicated-IP plan fits, since you control ports and DNS. - Just for yourself, no domain: skip the proxy and reach it over an SSH tunnel —
ssh -L 5678:127.0.0.1:5678 user@server, then openlocalhost:5678. Works fine on a NAT VPS.
Lock it down and keep it up
n8n holds your API keys and credentials, so the box must be tight: do the security checklist (SSH keys, firewall, no password login) before you put real credentials in. restart: always in the compose file already means Docker brings n8n back after a crash or reboot — that's your uptime handled.
Is it worth it?
Be honest with yourself about volume. If you run automations constantly, self-hosting wins on cost (flat vs per-execution) and removes any workflow/run limits. If you trigger a few flows a month, a hosted seat is less hassle. But the control argument stands regardless: your workflows, your data, your keys — on your server, not rented. For most people who got serious about n8n, that's the deciding factor.
A 2 GB box, a compose file, a domain (or a tunnel), and you're running your own automation hub — payable in crypto with no KYC, live in minutes.