How to Self-Host n8n on a VPS with Docker Compose (2026)

Deploy n8n on your own VPS with Docker Compose, Postgres 18, task runners and Caddy HTTPS, using the current n8n 2.x settings, plus backups, safe updates and sizing from n8n's docs.

HourlyVPS blog cover reading Self-host n8n on a VPS, for a guide to running n8n with Docker Compose, Postgres and Caddy HTTPS

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

  1. Deploy an Ubuntu 24.04 LTS VPS with 2 to 4 GB of RAM and secure SSH access.
  2. Point a subdomain such as n8n.example.com at its IPv4 address and open only ports 22, 80 and 443.
  3. Install Docker Engine with the Docker Compose v2 plugin.
  4. Create an .env file with a pinned N8N_VERSION, an N8N_ENCRYPTION_KEY and database secrets.
  5. Write compose.yaml with four services (Caddy, n8n, Postgres 18 and the n8nio/runners sidecar) and publish only Caddy’s ports.
  6. Start the stack with docker compose up -d, then create the owner account and turn on two-factor authentication.
  7. Schedule nightly backups of the database, the .n8n volume 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.

ItemWhat to useWhy
A Linux VPSUbuntu 24.04 LTS with 2–4 GB RAM (see sizing below); Debian 12 works the same wayn8n ships as a Docker image, so the distribution matters little
A domain or subdomainAn A record such as n8n → your server’s IPv4 addressCaddy gets a Let’s Encrypt certificate for this name automatically; n8n’s guides serve n8n on a dedicated subdomain
Docker Engine + Compose v2The docker compose plugin, not the old standalone docker-compose binaryn8n’s install scripts check for Compose v2 specifically
Open ports22 (SSH), 80 and 44380 and 443 serve Caddy’s certificate challenge and HTTPS; nothing else needs to be public
Prerequisites based on n8n’s Docker Compose and Hetzner guides; Ubuntu 24.04 LTS is our default target OS.

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.

WorkloadSuggested planRunsReasoning
Try n8n for an afternoon (SQLite, one user)Quartz Q2 (1 vCPU, 2 GB)A few hours, then deleteIdle 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/7n8n 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 APIsQuartz Q4 (2 vCPU, 4 GB)24/7Covers n8n’s documented 2 GB upper bound with room left for Postgres, the runner and the OS.
n8n Assistant with the bundled code sandboxQuartz Q4 as the floor; Quartz Q8 (4 vCPU, 8 GB) for headroom24/7n8n 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 experimentsQuartz Q8, or Chrono C8 (2 dedicated vCPUs, 8 GB)24/7Add RAM first. Dedicated vCPUs only pay off if Code-node work keeps the CPU busy for long stretches.
Documented figures from n8n’s sizing and Docker Compose docs. The plan choices are our reasoning from those figures, not benchmark results. Every plan bills by the hour; a 24/7 server’s charges stop at its monthly price for the rest of each billing period (one month from your order date).

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 PGDATA line. 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/n8n and n8nio/runners both read N8N_VERSION; n8n requires the runner image version to match. The runner also gets GENERIC_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

  1. 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 with N8N_INSTANCE_OWNER_MANAGED_BY_ENV and a bcrypt password hash.)
  2. 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.
  3. 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).
  4. 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.
  5. Optional: configure SMTP with N8N_EMAIL_MODE=smtp and the N8N_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.

VariableValue in this stackWhat it does
N8N_HOSTn8n.example.comHost name n8n runs on. Default: localhost.
N8N_PROTOCOLhttpsProtocol used to reach n8n. Default: http.
N8N_PORT5678The HTTP port n8n listens on inside the container; Caddy proxies to it.
N8N_WEBHOOK_URLhttps://n8n.example.com/Public base URL for test and production webhooks. Replaces WEBHOOK_URL, deprecated from n8n 2.35.0; the old name still works but logs a warning at startup.
N8N_PROXY_HOPS1Number of reverse proxies in front of n8n. Default: 0.
GENERIC_TIMEZONE + TZAmerica/New_YorkGENERIC_TIMEZONE sets the timezone for the Schedule Trigger and other schedule nodes (default America/New_York); TZ sets the container’s system clock.
N8N_ENCRYPTION_KEY64 hex charactersEncrypts credentials in the database. Default: a random key n8n generates on first launch and saves in .n8n/config.
N8N_RUNNERS_MODE + N8N_RUNNERS_AUTH_TOKENexternal + a shared secretRuns Code-node JavaScript and Python in the separate n8nio/runners container instead of a child process.
DB_TYPE + DB_POSTGRESDB_*postgresdbSwitches storage from SQLite to Postgres. n8n supports Postgres 16, 17 and 18 as of July 2026.
Defaults and descriptions from n8n’s deployment and endpoint variable references, n8n 2.41.

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 habitStatus in n8n 2.xUse instead
WEBHOOK_URLDeprecated from n8n 2.35.0; still works but logs a warningN8N_WEBHOOK_URL
N8N_BASIC_AUTH_ACTIVE, N8N_BASIC_AUTH_USER, N8N_BASIC_AUTH_PASSWORDRemoved in n8n 1.0; there is no supported way to switch the login screen offThe owner account plus 2FA
N8N_RUNNERS_ENABLED=trueDeprecated from n8n 2.0, where runners are on by defaultN8N_RUNNERS_MODE=external plus a runner sidecar
External runner started from the n8nio/n8n imageRemoved from the main image in n8n 2.0The separate n8nio/runners image, same tag
Pyodide-based Python in the Code nodeRemoved in n8n 2.0Native Python on external-mode runners
Activate toggle, update:workflow --activeReplaced in n8n 2.0; update:workflow is deprecatedPublish / Unpublish, publish:workflow, unpublish:workflow
image: n8nio/n8n with no tagn8n’s 2.0 notes recommend pinning a versionn8nio/n8n:${N8N_VERSION}
npm install n8n or npx n8nNot available for new installs from n8n 3.0Docker: a Compose file or n8n’s one-line setup
From n8n’s 2.0 breaking changes, endpoint and user management references, CLI docs and one-line setup page, as of n8n 2.41.6.

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:

  1. Run sudo ./backup.sh.
  2. Edit N8N_VERSION in .env to the new stable release. Both the n8n and runner images follow it.
  3. 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

SymptomLikely causeFix
Certificate warning, or the site doesn’t loadDNS doesn’t point at the server yet, or ports 80/443 are blockedCheck getent hosts, sudo ufw status and sudo docker compose logs caddy, then sudo docker compose restart caddy.
Webhook URLs in the editor show http://localhost:5678N8N_WEBHOOK_URL is missing or wrongSet it to https://your-host/ and run sudo docker compose up -d to recreate n8n.
Startup log warns that WEBHOOK_URL is deprecatedn8n 2.35.0 renamed the variableRename it to N8N_WEBHOOK_URL.
Login fails on http://SERVER_IP:5678N8N_SECURE_COOKIE defaults to true, so session cookies only travel over HTTPSUse the HTTPS hostname. Don’t publish port 5678 to work around it.
Code node errors or hangs until it times outRunner container down, RUNNERS_AUTH_TOKEN mismatch, or runner and n8n versions differCheck sudo docker compose logs n8n-runner; both images must use ${N8N_VERSION}.
n8n won’t start and logs Mismatching encryption keysN8N_ENCRYPTION_KEY differs from the key saved in .n8n/configRestore the original key from your backed-up .env. Never generate a new key for an existing instance.
Postgres logs database files are incompatible with serverThe Postgres image moved to a new major versionPin the old tag again, then upgrade with a dump and restore.
n8n may have run out of memory, 503 errors or Connection LostA workflow holds more data in memory than the server hasProcess data in batches with Loop Over Items and sub-workflows, avoid manual runs on big data, set N8N_CONCURRENCY_PRODUCTION_LIMIT, or move to a plan with more RAM.
Disk use keeps growingSaved execution dataLower EXECUTIONS_DATA_MAX_AGE or save only failed runs with EXECUTIONS_DATA_SAVE_ON_SUCCESS=none.
Owner password lost and no SMTP configuredNo email reset pathsudo docker compose exec -u node n8n n8n user-management:reset returns user management to its pre-setup state and removes all user accounts. Last resort only.
Lost the 2FA device and the recovery codesNo second factor leftsudo docker compose exec -u node n8n n8n mfa:disable --email=you@example.com, then log in and set up 2FA again.
Causes and fixes from n8n’s environment variable, memory, execution data, concurrency, CLI and withPostgres documentation; the encryption-key message is quoted from n8n’s source code.

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.

OptionMonthly priceExecutionsWho runs the server
n8n Cloud Starter20 € (billed annually)2,500 per month, 5 at a timen8n
n8n Cloud Pro50 € (billed annually)10,000 per month, up to 50 at a timen8n
Self-hosted Business license667 € (billed annually), plus your server40,000 per monthYou
Self-hosted Community on Quartz Q2No license fee + $10.00/month capNo quota listed for the Community edition; your server’s RAM and CPU set the limitYou
Self-hosted Community on Quartz Q4No license fee + $15.00/month capNo quota listed for the Community edition; your server’s RAM and CPU set the limitYou
n8n prices and limits from n8n.io/pricing as of October 3, 2026, with annual billing; monthly billing costs more. HourlyVPS figures are each plan’s monthly cap, the most an always-on server costs in a billing period, and render from our live price list.

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.

Quartz Q4 (2 vCPU, 4 GB): a 3-hour trial, a day, a week and a month of n8n
DurationHours on the meterCost $0.03/hour · cap $15.00/monthNote
3 hours3$0.09
1 day24$0.72
7 days168$5.04
30 days720$15.00Capped at the monthly price
Charges stop at $15.00 after 500 hours (about 20.8 days) in a billing period; the rest of that period is free.

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 Quartz Q4 Plan details for Quartz Q4

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
Deploy Quartz Q4

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.

FAQ

Is self-hosted n8n free and open source?

It's free to self-host: the Community edition has no license fee under n8n's Sustainable Use License, which allows internal business, personal and client-automation use. It's source-available rather than open source, because the license restricts commercial use, and hosting n8n so that clients build their own workflows needs a commercial agreement.

What are the minimum VPS requirements for n8n?

n8n's example sizing, based on n8n Cloud, puts n8n itself at 320 MB to 2 GB of memory, about 100 MB when idle, and says memory matters more than CPU. With Postgres, a task runner and Caddy on the same server, plan on 2 GB for light use and 4 GB for production; the n8n Assistant sandbox needs at least 4 GB RAM and 2 vCPUs.

Do I need a domain name to self-host n8n?

In practice, yes. n8n's session cookies only travel over HTTPS by default, and n8n's own guides serve it on a dedicated subdomain with a Let's Encrypt certificate, so point a subdomain you control at the server.

Should n8n use SQLite or Postgres?

SQLite is the default and fine for trying n8n. n8n recommends Postgres for production instances with more than a handful of users or workflows running around the clock, and supports Postgres 16, 17 and 18 as of July 2026.

How do I update n8n running in Docker Compose?

Back up first, change the pinned version tag, then run docker compose pull, docker compose down and docker compose up -d. n8n suggests updating at least monthly and reading the release notes for breaking changes.

Can I use n8n's one-line installer on a VPS?

It works, but it's built for a local machine: it publishes port 5678 over plain HTTP, uses SQLite and starts the n8n Assistant sandbox, which n8n says needs at least 4 GB RAM and 2 vCPUs. On a public VPS, add HTTPS and a firewall, or use a Compose file like the one in this guide.

Does n8n have to run 24/7?

For schedules and webhooks, yes. n8n notes that executions missed while an instance is down or restarting are not recoverable, so keep production instances on around the clock. On HourlyVPS that costs at most the plan's monthly price per billing period, while trials and test restores cost only their hours.

Sources

  1. Use Docker Compose (server setup)n8n Docs · docs.n8n.io · checked
  2. Install using Docker Composen8n Docs · docs.n8n.io · checked
  3. Configure webhook URLs with reverse proxyn8n Docs · docs.n8n.io · checked
  4. Set up task runnersn8n Docs · docs.n8n.io · checked
  5. n8n v2.0 breaking changesn8n Docs · docs.n8n.io · checked
  6. Back up and restoren8n Docs · docs.n8n.io · checked
  7. Prerequisites: infrastructure sizingn8n Docs · docs.n8n.io · checked
  8. License FAQ (Sustainable Use License)n8n Docs · docs.n8n.io · checked
  9. Port publishing and mappingDocker Docs · docs.docker.com · checked
  10. n8n plans and pricingn8n · n8n.io · checked
All posts