To self-host n8n on a VPS, point a subdomain at the server, run n8n, PostgreSQL and n8n’s task-runner sidecar with Docker Compose, and put Caddy in front to handle HTTPS. On current n8n 2.x (2.41.6 was the stable release on October 2, 2026), the settings that make or break a proxied install are N8N_HOST, N8N_PROTOCOL, N8N_WEBHOOK_URL, N8N_PROXY_HOPS, GENERIC_TIMEZONE and N8N_ENCRYPTION_KEY.
Every command and variable below comes from n8n’s official documentation and reference Compose files, current as of n8n 2.41.6. The guide also covers what most tutorials skip: external task runners, a port layout your firewall can actually enforce, backups that include the encryption key, and rehearsing restores and upgrades on a throwaway hourly server.
Key takeaways
- Behind a reverse proxy, set N8N_WEBHOOK_URL (WEBHOOK_URL is deprecated from n8n 2.35.0) and N8N_PROXY_HOPS=1, or webhook URLs come out wrong.
- n8n's docs say to run task runners in external mode in production; the n8nio/runners sidecar must use the same version tag as n8nio/n8n.
- Don't publish port 5678: Docker-published ports bypass ufw, so let Caddy reach n8n over the private Compose network.
- A complete backup is the database plus the .n8n folder with the encryption key; without the key, restored credentials can't be decrypted.
- n8n's example sizing gives n8n itself 320 MB to 2 GB of memory; the n8n Assistant sandbox needs at least 4 GB RAM and 2 vCPUs.
How to self-host n8n on a VPS: the short version
- Deploy an Ubuntu 24.04 LTS VPS with 2 to 4 GB of RAM and secure SSH access.
- Point a subdomain such as
n8n.example.comat its IPv4 address and open only ports 22, 80 and 443. - Install Docker Engine with the Docker Compose v2 plugin.
- Create an
.envfile with a pinnedN8N_VERSION, anN8N_ENCRYPTION_KEYand database secrets. - Write
compose.yamlwith four services (Caddy, n8n, Postgres 18 and then8nio/runnerssidecar) and publish only Caddy’s ports. - Start the stack with
docker compose up -d, then create the owner account and turn on two-factor authentication. - Schedule nightly backups of the database, the
.n8nvolume and.env, and copy them off the server.
What do you need before you start?
n8n’s own Docker Compose server guide says self-hosting needs real skills: running servers and containers, managing resources, securing applications and configuring n8n. It recommends n8n Cloud to anyone who isn’t comfortable with that. If you are, this is the shopping list.
| Item | What to use | Why |
|---|---|---|
| A Linux VPS | Ubuntu 24.04 LTS with 2–4 GB RAM (see sizing below); Debian 12 works the same way | n8n ships as a Docker image, so the distribution matters little |
| A domain or subdomain | An A record such as n8n → your server’s IPv4 address | Caddy gets a Let’s Encrypt certificate for this name automatically; n8n’s guides serve n8n on a dedicated subdomain |
| Docker Engine + Compose v2 | The docker compose plugin, not the old standalone docker-compose binary | n8n’s install scripts check for Compose v2 specifically |
| Open ports | 22 (SSH), 80 and 443 | 80 and 443 serve Caddy’s certificate challenge and HTTPS; nothing else needs to be public |
Start from a hardened server: a sudo user, SSH keys, root login and password logins disabled, and automatic security updates on. Our guides on connecting to a VPS over SSH and securing a new Linux VPS cover that. For Docker itself, follow how to install Docker on a VPS, which uses Docker’s official apt repository and includes the Compose plugin. Check both are present:
docker --version
docker compose version
n8n 3.0 is Docker-only. n8n’s one-line setup page says the npm install n8n route no longer works for new installs from n8n 3.0, announced for October 2026, and that n8n will be distributed only through Docker. Learning the Compose setup now saves a migration later.
How much RAM does n8n need on a VPS?
n8n itself needs little; the database, the task runner and AI features add up. n8n’s sizing guidance, an illustrative example based on n8n Cloud, says an idle instance needs about 100 MB and lists 320 MB to 2 GB of memory and 512 MB to 4 GB of SSD for the database. It also notes that n8n is not CPU-intensive: memory runs out before CPU does. Large JSON payloads, binary files, Code nodes and manual test runs are what push memory up, according to n8n’s memory troubleshooting page.
| Workload | Suggested plan | Runs | Reasoning |
|---|---|---|---|
| Try n8n for an afternoon (SQLite, one user) | Quartz Q2 (1 vCPU, 2 GB) | A few hours, then delete | Idle n8n is about 100 MB; 2 GB leaves room for Docker, Caddy and test runs. Skip the n8n Assistant sandbox at this size, and delete the server when done. |
| Personal automations 24/7 (Postgres, task runner, light payloads) | Quartz Q2 (1 vCPU, 2 GB) | 24/7 | n8n at the low end of its 320 MB–2 GB range plus Postgres fits in 2 GB. Watch memory if workflows move files. |
| Small team in production, Code nodes, AI agent workflows that call LLM APIs | Quartz Q4 (2 vCPU, 4 GB) | 24/7 | Covers n8n’s documented 2 GB upper bound with room left for Postgres, the runner and the OS. |
| n8n Assistant with the bundled code sandbox | Quartz Q4 as the floor; Quartz Q8 (4 vCPU, 8 GB) for headroom | 24/7 | n8n documents at least 4 GB RAM and 2 vCPUs, because the sandbox runs Docker-in-Docker. For production, n8n recommends the Daytona sandbox provider over the bundled one. |
| Large files, high concurrency, queue-mode experiments | Quartz Q8, or Chrono C8 (2 dedicated vCPUs, 8 GB) | 24/7 | Add RAM first. Dedicated vCPUs only pay off if Code-node work keeps the CPU busy for long stretches. |
Disk is rarely the limit. n8n prunes finished executions by default once they are older than 336 hours (14 days) or the count passes 10,000, whichever comes first. Running local language models next to n8n, as n8n’s AI starter kit does with Ollama, is a different sizing problem; see how to size a VPS for AI agents.
On a 2 GB server, cap parallel runs. n8n’s concurrency docs say regular mode doesn’t limit how many production executions run at the same time, and that too many at once can make n8n unresponsive. Add N8N_CONCURRENCY_PRODUCTION_LIMIT with a small value, such as 5, to the n8n service in the Compose file below: executions over the limit wait in a queue and run in order.
Deploy n8n with Docker Compose, Postgres and Caddy
The stack below combines four official references: the environment block from n8n’s Docker Compose server guide, the Postgres service from n8n’s Docker Compose install page, the external task runner from n8n’s withPostgres example, and the Caddyfile from n8n’s Hetzner guide. The Caddy service follows the Compose example in the official Caddy image docs. Run every command as your sudo user, not root.
Step 1: Point DNS at the server and open the firewall
Create an A record for your subdomain (for example n8n → the server’s IPv4 address) at your DNS provider. Caddy requests the certificate on first start, so wait until the name resolves to your server:
getent hosts n8n.example.com
Then allow SSH, HTTP and HTTPS. Allow SSH first so you don’t lock yourself out:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443
sudo ufw enable
Docker bypasses ufw for published ports. Docker’s firewall documentation says traffic to a published container port is diverted before ufw’s rules apply, and its port publishing page says ports are published on all host addresses by default. That is why the Compose file below publishes only Caddy’s ports 80 and 443. n8n, Postgres and the task runner have no ports: entry, so they are reachable only on the private Compose network.
Step 2: Create the project folder and the .env file
n8n’s guide keeps everything in one project folder. Create it with a subfolder for the Caddy config:
mkdir -p ~/n8n-compose/caddy
cd ~/n8n-compose
Now write .env. This version generates the secrets with openssl rand; set DOMAIN_NAME, SUBDOMAIN and GENERIC_TIMEZONE to your own values (for example Europe/Istanbul). N8N_VERSION is pinned on purpose: n8n’s 2.0 release notes recommend pinning a specific version, and the task-runner image must use the same tag as n8n. Check the current stable number on n8n’s GitHub releases page.
cat > .env <<EOF
N8N_VERSION=2.41.6
DOMAIN_NAME=example.com
SUBDOMAIN=n8n
GENERIC_TIMEZONE=America/New_York
N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
RUNNERS_AUTH_TOKEN=$(openssl rand -hex 32)
POSTGRES_USER=n8n
POSTGRES_PASSWORD=$(openssl rand -hex 24)
POSTGRES_DB=n8n
EOF
chmod 600 .env
Guard N8N_ENCRYPTION_KEY like a master password. n8n encrypts every stored credential with this key. n8n only uses a custom key if none is saved in its settings file yet, so set it before the first start. Keep a copy off the server, and never change it on an instance that already holds credentials.
Step 3: Write compose.yaml
Save this as ~/n8n-compose/compose.yaml (for example with nano compose.yaml):
services:
caddy:
image: caddy:2
restart: unless-stopped
cap_add:
- NET_ADMIN
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./caddy:/etc/caddy
- caddy_data:/data
- caddy_config:/config
depends_on:
- n8n
postgres:
image: postgres:18
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
PGDATA: /var/lib/postgresql/data
volumes:
- db_storage:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
n8n:
image: n8nio/n8n:${N8N_VERSION}
restart: always
environment:
- N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- N8N_WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
- N8N_PROXY_HOPS=1
- NODE_ENV=production
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_RUNNERS_MODE=external
- N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
volumes:
- n8n_storage:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
n8n-runner:
image: n8nio/runners:${N8N_VERSION}
restart: always
environment:
- N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
- N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
depends_on:
- n8n
volumes:
caddy_data:
caddy_config:
db_storage:
n8n_storage:
Four details in this file matter more than they look:
- No ports on n8n. Caddy reaches n8n by service name on the Compose network, so port 5678 is never exposed to the internet, whatever ufw says.
- The
PGDATAline. n8n’s install page warns that Postgres 18 changed its default data location. Without this line, Postgres writes outside the volume and your database starts empty after a re-create. For a dedicated non-root database user, add the init script from n8n’s withPostgres example. - The runner sidecar. n8n’s task runner docs say to always use task runners in production, in external mode: they are the only isolation layer between Code-node code and n8n’s database, encryption key and credentials. Internal mode is deprecated from n8n 3.0, and since n8n 2.0 Python in the Code node runs only on external runners.
- One version for two images.
n8nio/n8nandn8nio/runnersboth readN8N_VERSION; n8n requires the runner image version to match. The runner also getsGENERIC_TIMEZONE, a documented runner variable, so dates in Code nodes use your timezone.
Only want a quick SQLite trial? Delete the postgres service, the six DB_ lines and the depends_on block under n8n. n8n then keeps its SQLite database inside the n8n_storage volume. n8n’s install page calls SQLite fine for trying things out and recommends Postgres for production instances with more than a handful of users or round-the-clock workflows.
Step 4: Add the Caddyfile and start the stack
This is the Caddyfile from n8n’s own Caddy setup. Replace the hostname with yours. flush_interval -1 switches Caddy’s proxy into low-latency mode, which turns off response buffering so streamed responses reach the editor immediately.
cat > caddy/Caddyfile <<'EOF'
n8n.example.com {
reverse_proxy n8n:5678 {
flush_interval -1
}
}
EOF
Caddy’s reverse_proxy directive sets the X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host headers by default, which are exactly the headers n8n’s reverse-proxy page asks the last proxy to send. No extra header config is needed.
For how Caddy obtains and renews certificates, see our Caddy reverse proxy guide. Prefer Traefik? n8n’s generic Compose guide uses Traefik with a TLS challenge; the n8n environment block stays the same.
Start everything and check the containers:
sudo docker compose up -d
sudo docker compose ps
After a minute, ask n8n whether it is ready. /healthz/readiness returns HTTP 200 only when n8n is up and its database is connected and migrated:
curl -s -o /dev/null -w '%{http_code}\n' https://n8n.example.com/healthz/readiness
If you get anything other than 200, read the logs (press Ctrl+C to stop following them):
sudo docker compose logs -f n8n caddy
Step 5: Create the owner account and lock it down
- Open
https://n8n.example.com. n8n shows the owner setup screen. Do this right away: on a fresh instance, the first person to complete that screen becomes the owner. (From n8n 2.17.0 you can instead pre-provision the owner withN8N_INSTANCE_OWNER_MANAGED_BY_ENVand a bcrypt password hash.) - Choose a password of at least eight characters with at least one number and one capital letter (n8n’s minimum); a password manager makes a long one painless.
- Turn on two-factor authentication under Settings > Personal > Enable 2FA, scan the QR code with an authenticator app and save the recovery codes (n8n 2FA docs).
- Optional: register the free Community edition under Settings > Usage and plan > Unlock. Per n8n’s edition comparison, the free license key adds folders, debug in editor and custom execution data.
- Optional: configure SMTP with
N8N_EMAIL_MODE=smtpand theN8N_SMTP_*variables from n8n’s user management docs. Without SMTP you can still copy invite links by hand, but users can’t reset passwords by email. Send through an authenticated relay on port 587 or 465: outbound port 25 is closed on new HourlyVPS servers, as our acceptable use policy explains.
Then run n8n’s built-in security audit, which lists unused credentials, risky nodes and file-system access:
sudo docker compose exec -u node n8n n8n audit
Saving is not publishing in n8n 2.x. n8n autosaves your edits as a draft; a workflow goes live only when you select Publish, and production runs use the published version. Production webhook URLs under /webhook/ answer for published workflows, while /webhook-test/ URLs work while you listen for a test event in the editor.
Which environment variables does n8n need behind a reverse proxy?
n8n builds webhook URLs from N8N_PROTOCOL, N8N_HOST and N8N_PORT. Behind a proxy that gives the wrong result, because n8n listens on 5678 while the public side is 443. n8n’s reverse-proxy page therefore asks for two extra settings: the public webhook URL and N8N_PROXY_HOPS=1.
| Variable | Value in this stack | What it does |
|---|---|---|
N8N_ | n8n. | Host name n8n runs on. Default: localhost. |
N8N_ | https | Protocol used to reach n8n. Default: http. |
N8N_ | 5678 | The HTTP port n8n listens on inside the container; Caddy proxies to it. |
N8N_ | https: | Public base URL for test and production webhooks. Replaces WEBHOOK_, deprecated from n8n 2.35.0; the old name still works but logs a warning at startup. |
N8N_ | 1 | Number of reverse proxies in front of n8n. Default: 0. |
GENERIC_ + TZ | America/ | GENERIC_ sets the timezone for the Schedule Trigger and other schedule nodes (default America/); TZ sets the container’s system clock. |
N8N_ | 64 hex characters | Encrypts credentials in the database. Default: a random key n8n generates on first launch and saves in .n8n/. |
N8N_ + N8N_ | external + a shared secret | Runs Code-node JavaScript and Python in the separate n8nio/ container instead of a child process. |
DB_ + DB_ | postgresdb | Switches storage from SQLite to Postgres. n8n supports Postgres 16, 17 and 18 as of July 2026. |
Outdated settings in older n8n tutorials
Many n8n tutorials online were written for n8n 1.x. If one uses anything in the left column, translate it before you copy it:
| Old setting or habit | Status in n8n 2.x | Use instead |
|---|---|---|
WEBHOOK_ | Deprecated from n8n 2.35.0; still works but logs a warning | N8N_ |
N8N_, N8N_, N8N_ | Removed in n8n 1.0; there is no supported way to switch the login screen off | The owner account plus 2FA |
N8N_ | Deprecated from n8n 2.0, where runners are on by default | N8N_ plus a runner sidecar |
External runner started from the n8nio/ image | Removed from the main image in n8n 2.0 | The separate n8nio/ image, same tag |
| Pyodide-based Python in the Code node | Removed in n8n 2.0 | Native Python on external-mode runners |
Activate toggle, update: | Replaced in n8n 2.0; update: is deprecated | Publish / Unpublish, publish:, unpublish: |
image: n8nio/ with no tag | n8n’s 2.0 notes recommend pinning a version | n8nio/ |
npm install n8n or npx n8n | Not available for new installs from n8n 3.0 | Docker: a Compose file or n8n’s one-line setup |
How do you back up self-hosted n8n?
n8n’s backup docs define a complete backup as two parts: the .n8n user folder, which holds the config file with the encryption key, and your external database. The CLI’s export:workflow and export:credentials commands are handy for moving workflows, but they skip users, execution history, variables and instance settings, so they can’t rebuild an instance on their own.
A nightly backup script
This script captures what this stack needs: a Postgres dump, a tarball of the n8n_storage volume and your config files, including .env with the encryption key. The volume copy uses the --volumes-from pattern from Docker’s volume backup docs, which matches n8n’s own advice to take nightly backups by attaching a second container to the data. Save it as ~/n8n-compose/backup.sh and replace YOUR_USER with your user name, here and in the cron line below:
#!/bin/sh
# n8n backup: Postgres dump + .n8n volume + config files
set -eu
APP_DIR=/home/YOUR_USER/n8n-compose
cd "$APP_DIR"
STAMP=$(date +%F-%H%M)
mkdir -p backups
docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' > "backups/n8n-db-$STAMP.dump"
docker run --rm --volumes-from "$(docker compose ps -q n8n)" -v "$PWD/backups:/backup" ubuntu tar czf "/backup/n8n-home-$STAMP.tgz" -C /home/node .n8n
tar czf "backups/n8n-config-$STAMP.tgz" .env compose.yaml caddy/Caddyfile
chmod 600 backups/*
find backups -name 'n8n-*' -mtime +14 -delete
Make it executable, run it once by hand and check the three files:
chmod 700 backup.sh
sudo ./backup.sh
ls -lh backups
Schedule it in root’s crontab with sudo crontab -e. This line runs it at 03:15 server time every night:
15 3 * * * /home/YOUR_USER/n8n-compose/backup.sh >> /var/log/n8n-backup.log 2>&1
Copy backups off the server
A backup on the same disk doesn’t survive a deleted or broken server. Pull the files to another machine every night with rsync over SSH, or push them to object storage, encrypted, as our VPS backup guide with restic shows. Treat them as secrets: the database dump holds encrypted credentials and the config tarball holds the key that decrypts them, so store them encrypted or in separate places. A provider snapshot before an upgrade is a useful extra rollback point, not a substitute for off-server copies.
Test the restore on an hourly server
A backup you have never restored is a hope. Deploy an hourly Quartz Q4 at $0.03/hour, install Docker, open ports 80 and 443, copy the three newest backup files to your home folder, and restore. A three-hour drill costs $0.09. The same steps later move a trial instance to the server that will run it around the clock.
Unpack the config into a new project folder. Before you start anything, change SUBDOMAIN in .env and the hostname in caddy/Caddyfile to a test name such as n8n-test, and create its DNS record:
mkdir -p ~/n8n-compose && cd ~/n8n-compose
tar xzf ~/n8n-config-2026-10-03-0315.tgz
nano .env caddy/Caddyfile
Create the containers and volumes without starting them, then unpack the .n8n folder into the n8n volume:
sudo docker compose create
sudo docker run --rm --volumes-from "$(sudo docker compose ps -aq n8n)" -v "$HOME:/backup" ubuntu tar xzf /backup/n8n-home-2026-10-03-0315.tgz -C /home/node
Start only Postgres, wait until it is healthy, and load the dump:
sudo docker compose up -d --wait postgres
sudo docker compose exec -T postgres sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists' < ~/n8n-db-2026-10-03-0315.dump
A restored copy would run your published workflows too: schedules would fire twice, and triggers that register webhooks with outside services could point them at the test hostname. On a drill server, unpublish everything with n8n’s CLI before n8n starts:
sudo docker compose run --rm --no-deps n8n unpublish:workflow --all
sudo docker compose up -d
Log in with your normal owner account and open one credential: if it decrypts, the key and the database match. Then delete the server. A stopped server is still billed, because its vCPU, memory, disk and IP addresses stay reserved for you; only deleting the server stops billing. Our checklist before you delete a VPS covers what to copy off first.
How do you update self-hosted n8n?
n8n releases a new minor version most weeks, and its update guide asks self-hosters to update at least once a month so they never jump several versions at once. Read the release notes for breaking changes first, then:
- Run
sudo ./backup.sh. - Edit
N8N_VERSIONin.envto the new stable release. Both the n8n and runner images follow it. - Pull the new images and recreate the containers, as n8n’s Docker docs describe:
cd ~/n8n-compose
sudo docker compose pull
sudo docker compose down
sudo docker compose up -d
Check /healthz/readiness again and scan sudo docker compose logs n8n for deprecation warnings. For a big jump, such as the move to n8n 3.0, rehearse first: restore last night’s backup on an hourly server as above, bump the version there, open your key workflows, then delete the server. n8n’s update guide recommends testing updates on a test instance first; with hourly billing that instance costs a few hours instead of a month.
Two upgrade traps. Don’t bump postgres:18 to a newer major tag the same way: Postgres can’t open a data directory written by an older major version and refuses to start with database files are incompatible with server. Your data stays intact; pin the old tag back and follow the dump-and-restore upgrade in n8n’s withPostgres README. And never run docker compose down -v: -v deletes the volumes, including the one holding your encryption key.
Troubleshooting self-hosted n8n
| Symptom | Likely cause | Fix |
|---|---|---|
| Certificate warning, or the site doesn’t load | DNS doesn’t point at the server yet, or ports 80/443 are blocked | Check getent hosts, sudo ufw status and sudo docker compose logs caddy, then sudo docker compose restart caddy. |
Webhook URLs in the editor show http: | N8N_ is missing or wrong | Set it to https: and run sudo docker compose up -d to recreate n8n. |
Startup log warns that WEBHOOK_ is deprecated | n8n 2.35.0 renamed the variable | Rename it to N8N_. |
Login fails on http: | N8N_ defaults to true, so session cookies only travel over HTTPS | Use the HTTPS hostname. Don’t publish port 5678 to work around it. |
| Code node errors or hangs until it times out | Runner container down, RUNNERS_ mismatch, or runner and n8n versions differ | Check sudo docker compose logs n8n-runner; both images must use ${N8N_. |
n8n won’t start and logs Mismatching encryption keys | N8N_ differs from the key saved in .n8n/ | Restore the original key from your backed-up .env. Never generate a new key for an existing instance. |
Postgres logs database files are incompatible with server | The Postgres image moved to a new major version | Pin the old tag again, then upgrade with a dump and restore. |
n8n may have run out of memory, 503 errors or Connection Lost | A workflow holds more data in memory than the server has | Process data in batches with Loop Over Items and sub-workflows, avoid manual runs on big data, set N8N_, or move to a plan with more RAM. |
| Disk use keeps growing | Saved execution data | Lower EXECUTIONS_ or save only failed runs with EXECUTIONS_. |
| Owner password lost and no SMTP configured | No email reset path | sudo docker compose exec -u node n8n n8n user-management: returns user management to its pre-setup state and removes all user accounts. Last resort only. |
| Lost the 2FA device and the recovery codes | No second factor left | sudo docker compose exec -u node n8n n8n mfa:, then log in and set up 2FA again. |
Is self-hosted n8n free? License and monthly cost
The software is free; the server and your time are not. n8n’s Community edition runs under the Sustainable Use License, a fair-code license that n8n itself says is not open source in the OSI sense. n8n’s license FAQ draws the line like this:
- Allowed: running your own business’s automations, personal projects and research; running several instances; building automations for clients on your instance as long as they can’t create or edit workflows; charging for workflow building, consulting and training; using n8n behind the scenes in your own product.
- Not allowed without a commercial agreement: hosting n8n as a service where clients build workflows; letting end users build or configure workflows through your product, whether by UI, API, MCP or an AI agent; white-labeling n8n; using enterprise features without a license key.
Installing and maintaining n8n on a client’s own server is also allowed, as long as you don’t host it for them as well. If your plan sits near any of these lines, n8n asks you to email [email protected] before you launch.
| Option | Monthly price | Executions | Who runs the server |
|---|---|---|---|
| n8n Cloud Starter | 20 € (billed annually) | 2,500 per month, 5 at a time | n8n |
| n8n Cloud Pro | 50 € (billed annually) | 10,000 per month, up to 50 at a time | n8n |
| Self-hosted Business license | 667 € (billed annually), plus your server | 40,000 per month | You |
| Self-hosted Community on Quartz Q2 | No license fee + $10.00/month cap | No quota listed for the Community edition; your server’s RAM and CPU set the limit | You |
| Self-hosted Community on Quartz Q4 | No license fee + $15.00/month cap | No quota listed for the Community edition; your server’s RAM and CPU set the limit | You |
n8n Cloud is the better fit if nobody on your team wants to own updates, backups and security; n8n itself steers non-experts there. Self-hosting fits when you want no execution quota, your data on a server you control, or several instances for testing and production.
On HourlyVPS every server bills by the hour, so the only choice is how long it lives: delete trial, restore-drill and upgrade-rehearsal servers when you’re done, and leave the instance that runs your schedules on. n8n’s sizing notes are blunt about downtime: executions missed while an instance is down or restarting, such as Cron or Webhook triggers, can’t be recovered. Here is what a Quartz Q4 costs over typical n8n lifetimes; the hourly vs monthly guide explains why, with a monthly cap, hourly billing never costs more than a monthly plan.
| Duration | Hours on the meter | Cost $0.03 | Note |
|---|---|---|---|
| 3 hours | 3 | $0.09 | |
| 1 day | 24 | $0.72 | |
| 7 days | 168 | $5.04 | |
| 30 days | 720 | $15.00 | Capped at the monthly price |
Every server is billed by the hour: the plan’s hourly rate is deducted from the server’s prepaid balance for every hour it exists, powered on or off, until you delete it. A monthly VPS here is simply an always-on server with an automatic monthly cap, no contract and no monthly prepayment; compare every plan on the pricing page. Deploy close to the APIs and people your workflows talk to; HourlyVPS deploys in Istanbul today, with New York coming soon, and our locations page has the details. Istanbul plans include the monthly traffic allowance listed for each plan, prorated for a server that exists for part of a billing period.
Quartz Q4
KVM · 1 Gbps port · Istanbul · initial credit $5
- vCPU
- 2 shared
- RAM
- 4 GB
- NVMe
- 80 GB
- Traffic
- Istanbul: 4 TB/month
- Per hour$0.03/hour
- Per day (24 h)$0.72/day
- Monthly cap$15.00/month
Deploy this setup
Run self-hosted n8n 24/7 with Postgres and HTTPS
Quartz Q4 · 2 shared vCPU · 4 GB RAM · 80 GB NVMe · Istanbul
- Per hour$0.03/hour
- Per day (24 h)$0.72/day
- Monthly cap$15.00/monthFor this job
Starts with a $5 initial credit, which goes into the server’s balance and pays for its hours.
Billed by the hour, never more than $15.00 per billing period. Delete the server and billing stops.



