The NordBastion polar-bear mascot seated on a carved stone bench in a dark Nordic vault, a laptop on his lap and a large cyan holographic automation workflow floating above him — six rounded nodes joined by glowing curved wires — with the cyan N-shield leaning against the bench and a server rack beside him
How-to · Self-host·13 min read · 30 min hands-on

Self-host n8n on a VPS.
Your automation engine, your credentials, your metal.

Six steps from a bare Nordic VPS to a TLS-terminated n8n on your own domain — Docker Compose, Caddy, PostgreSQL, webhooks that actually fire. Executions bounded by your CPU, not by a plan tier, on a box that costs $3.90 a month. Tested on Debian 12 with n8n 2.x.

The six steps
  1. 01

    Provision

    VPS + an A record

  2. 02

    Install

    get.docker.com

  3. 03

    Compose

    n8n + Caddy + Postgres

  4. 04

    First boot

    Owner account + 2FA

  5. 05

    Webhooks

    WEBHOOK_URL

  6. 06

    Harden

    Prune, silence, back up

Before you start · Licence and cost

What you are actually installing. And what the licence lets you do with it.

n8n is a workflow automation engine: a visual canvas where you wire a trigger — a webhook, a schedule, a new row in a database, a message in a queue — into a chain of nodes that call APIs, transform data, branch on conditions, run arbitrary JavaScript or Python, and hand the result to whatever comes next. Several hundred integration nodes ship in the box, plus a generic HTTP Request node that covers everything else. Since the 1.x line it also carries AI Agent and LLM nodes, which is why a large share of the people installing it in 2026 are building agents rather than classic ETL plumbing.

The part that matters before you type a single command is the licence, because n8n is not open source in the OSI sense and the difference is not academic. The core ships under the Sustainable Use License: a non-exclusive, royalty-free, worldwide grant to use, copy, modify and distribute the software for internal business purposes and for personal or non-commercial use. What it withholds is the right to charge others for n8n or for a derivative of it — which is the clause that rules out building a paid "managed n8n" product on top. Separately, any file carrying .ee. in its name or .ee in its path is excluded from that licence entirely and requires a paid n8n Enterprise License.

Read plainly: running n8n on a VPS to automate your own company, your own clients' work delivered as a service you perform, or your own personal life, is inside the free grant and always has been. Selling n8n-as-a-hosting-product is not. Almost every "is n8n really free?" argument on the internet is two people talking past each other across that line.

The cost side. n8n Cloud is priced per execution: the Starter plan is €20 a month billed annually for 2,500 executions, Pro is €50 a month for 10,000, Business is €667 a month for 40,000. Self-hosted, the execution count is not a line item at all — it is bounded by how much CPU and memory the box has. A workflow that polls an API every five minutes burns 8,640 executions a month on its own; on Cloud that single workflow already forces the Pro tier, and on a $3.90 VPS it is a rounding error against idle CPU.

Option Monthly Executions included Who runs it
n8n Cloud · Starter€202 500n8n GmbH
n8n Cloud · Pro€5010 000n8n GmbH
n8n Cloud · Business€66740 000n8n GmbH
Self-hosted · Sentinel VPS$3.90CPU-bound, not meteredYou

Cloud prices as published on n8n.io in August 2026, billed annually; monthly billing costs more. The trade is not only money — self-hosting moves upgrades, backups, TLS renewal and uptime onto your side of the line.

Before you start · Sizing

Sizing the box. Four gigabytes is the floor, disk is the sleeper.

n8n's own Docker Compose documentation gives the minimum as 2 vCPU and 4 GB of RAM. That is not a marketing number: below it the editor front-end and a moderately branched workflow will fight each other for memory, and the first big JSON payload will take the container out with an out-of-memory kill.

Sentinel · 2 vCPU, 4 GB, 120 GB NVMe, $3.90/mo. The right default. Runs n8n, PostgreSQL and Caddy together with headroom for a personal or small-team instance doing a few hundred executions a day. Idle memory sits around 700 MB across the three containers.

Garrison · 4 vCPU, 8 GB, 240 GB NVMe, $7.90/mo. The step up when you add queue mode with Redis and one or two worker containers, when workflows routinely hold multi-megabyte payloads in memory, or when you want a comfortable margin for AI-agent workflows that fan out into several parallel branches.

Ravelin · 8 vCPU, 16 GB, 480 GB NVMe, $16.90/mo. A team instance with thousands of executions a day and binary-heavy work — PDF generation, image processing, audio transcription. Dedicated cores matter here because those workloads are CPU-bound rather than API-bound.

The disk is the part people forget. n8n stores the full input and output of every node of every execution. A chatty workflow running every minute writes hundreds of megabytes a week. The defaults do prune — EXECUTIONS_DATA_PRUNE is true, EXECUTIONS_DATA_MAX_AGE is 336 hours (fourteen days) and EXECUTIONS_DATA_PRUNE_MAX_COUNT is 10 000 — but fourteen days of a busy instance is still a lot of NVMe. Step 06 tightens those.

Step 01 · Provision

A Nordic VPS and one DNS record. In that order.

In the panel: Order → VPS → Sentinel, image Debian 12. No email address is required to open the account, no identity document is requested at any point, and the invoice is settled in Monero, Bitcoin, Lightning or any of the other supported assets. Pick the bastion by latency to the services you automate rather than to yourself — an automation server talks to APIs far more than it talks to you.

Then create the DNS record before you touch the server: an A record for n8n.example.com pointing at the VPS IPv4, and an AAAA record if you use IPv6. This has to be done first, because Caddy asks Let's Encrypt for a certificate the moment the stack starts, and a certificate request for a name that does not resolve fails — then backs off, and you spend twenty minutes wondering why the site is unreachable.

Give the DNS a minute to propagate and confirm it from your own machine before continuing:

dig +short n8n.example.com
# → the IPv4 of your VPS, and nothing else

Before anything listens on a public port, run the first-hour hardening checklist — key-only SSH, a firewall that allows 22, 80 and 443 and nothing else, and unattended security upgrades. An automation server is a credential vault; it deserves the full hour.

Step 02 · Install

Docker, and nothing else. One command.

SSH in and install the Docker Engine with the Compose v2 plugin:

apt update && apt install -y ca-certificates curl
curl -fsSL https://get.docker.com | sh
docker compose version

The convenience script installs Engine, CLI, containerd and the Compose plugin from Docker's own repository. The last line should print Docker Compose version v2 or higher; if it prints "docker: 'compose' is not a docker command" you have the distribution's older docker.io package installed and should remove it first.

There is a one-line n8n installer that wraps all of this, and it works. This guide writes the Compose file by hand instead, because everything you will later need to change — the encryption key, the database, the webhook URL, the retention policy, the worker count — lives in that file, and a stack you cannot read is a stack you cannot fix at 3 a.m.

Step 03 · Compose

Three files in /opt/n8n. n8n, PostgreSQL, Caddy.

Create the directory and generate the two secrets first. Generate them now, in this order, and paste them into the .env as you go — the encryption key in particular must exist before n8n's first boot, not after.

mkdir -p /opt/n8n && cd /opt/n8n
openssl rand -hex 32   # → N8N_ENCRYPTION_KEY
openssl rand -hex 24   # → POSTGRES_PASSWORD

/opt/n8n/.env

DOMAIN=n8n.example.com
LETSENCRYPT_EMAIL=you@example.com
GENERIC_TIMEZONE=Europe/Stockholm

N8N_ENCRYPTION_KEY=paste_the_32_byte_hex_here
POSTGRES_DB=n8n
POSTGRES_USER=n8n
POSTGRES_PASSWORD=paste_the_24_byte_hex_here

Lock the file down immediately — it holds the key to every credential the instance will ever store:

chmod 600 /opt/n8n/.env

/opt/n8n/docker-compose.yml

services:
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      - DOMAIN=${DOMAIN}
      - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10

  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      - N8N_HOST=${DOMAIN}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - N8N_EDITOR_BASE_URL=https://${DOMAIN}
      - WEBHOOK_URL=https://${DOMAIN}/
      - N8N_PROXY_HOPS=1
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${GENERIC_TIMEZONE}
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_BLOCK_ENV_ACCESS_IN_NODE=true
      - N8N_DIAGNOSTICS_ENABLED=false
      - N8N_VERSION_NOTIFICATIONS_ENABLED=false
      - N8N_PERSONALIZATION_ENABLED=false
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  caddy_data:
  caddy_config:
  pg_data:
  n8n_data:

/opt/n8n/Caddyfile

{$DOMAIN} {
    encode zstd gzip
    tls {$LETSENCRYPT_EMAIL}
    reverse_proxy n8n:5678
}

Two design decisions worth naming. First, n8n does not publish a port. Only Caddy binds 80 and 443; n8n listens on 5678 inside the Compose network where nothing outside the host can reach it. A surprising number of self-hosted n8n instances are on the public internet on port 5678 with no TLS in front, and search engines for exposed services index them. Second, the n8n data volume stays mounted even though PostgreSQL now holds the workflows — that directory still carries the instance settings, the log files and the source-control assets.

Bring it up:

cd /opt/n8n
docker compose up -d
docker compose logs -f caddy   # watch the certificate being issued
Step 04 · First boot

The owner account, and the key you must not lose. Read this one twice.

Open https://n8n.example.com. The first screen is the owner-account setup — email, password, name. There is no HTTP basic-auth environment variable to configure any more; user management has been built into n8n since the 1.x line, and the account you create here is the instance owner. Use a password from your password manager, then go straight to Settings → Personal → Two-factor authentication and turn it on. This login is the front door to every API key you will ever paste into a node.

The one irreversible mistake

n8n encrypts every stored credential with N8N_ENCRYPTION_KEY. If you do not set it, n8n generates one on first boot and writes it inside the data volume. Recreate that volume — a docker compose down -v, a migration to a new server, a botched restore — and the new instance generates a different key, every credential in the database becomes undecryptable, and there is no recovery path whatsoever. You re-enter each API key, OAuth token and password by hand. Set the key explicitly, as this guide does, and store a copy off the server.

The right home for that copy is a password manager you also control — the self-hosted Vaultwarden guide covers one, and the deliberate point is that it should not live on the same box as the thing it unlocks.

Verify the database is actually PostgreSQL and not the SQLite fallback — if the DB_ variables have a typo, n8n silently starts on SQLite and you find out three months later:

docker compose exec postgres psql -U n8n -d n8n -c '\dt' | head
# → a list of n8n tables (workflow_entity, credentials_entity, execution_entity…)
Step 05 · Webhooks

The half that silently does not work. Webhooks behind a proxy.

An n8n that boots, shows the editor and runs a manual test is not yet a working n8n. The half that breaks quietly is inbound webhooks, and it breaks in ways that look like the third-party service is at fault.

WEBHOOK_URL. Without it, n8n builds webhook URLs from N8N_HOST and N8N_PORT and hands you something like http://localhost:5678/webhook/abc — which you then paste into Stripe or GitHub, where it can never be reached. The Compose file above sets WEBHOOK_URL to the public HTTPS root, which is what the editor will display and what the outside world can actually call.

N8N_EDITOR_BASE_URL. The public URL n8n uses for links in the emails it sends — password resets, user invitations. Wrong here means an invitation link pointing at localhost, which is a support ticket from a colleague rather than a broken integration.

N8N_PROXY_HOPS. n8n reads the client IP from X-Forwarded-For, and it only trusts as many hops as this number says. With one reverse proxy in front — the Caddy in this stack — the value is 1. Put Cloudflare in front of Caddy and it becomes 2. Leave it at the default 0 and every request appears to come from the proxy itself, which quietly breaks rate limits and any IP-based logic in your workflows.

N8N_SECURE_COOKIE. It defaults to true, meaning the session cookie is only sent over HTTPS. That is the correct setting and this stack satisfies it. It is worth knowing because it explains the classic symptom of a first attempt over plain http: the login form accepts the password and then returns you to the login form, forever. The fix is TLS, not turning the flag off.

Test it properly. Create a workflow with a Webhook node, activate the workflow, copy the Production URL, then call it from a machine that is not the server:

curl -i https://n8n.example.com/webhook/<path>
# → HTTP/2 200, and a new execution visible in the editor

The distinction that trips everybody once: the Test URL only listens while you have the editor open with "Listen for test event" armed. The Production URL exists only when the workflow is toggled Active. A webhook that works in the editor and 404s in production is almost always an inactive workflow.

Step 06 · Harden

Prune, silence, back up. The three that decide whether it survives a year.

Retention. The defaults keep fourteen days or 10 000 executions, whichever comes first, with full input and output data for every node. On a small NVMe with a busy schedule trigger that is the thing that fills the disk. Add these to the n8n environment block and restart:

- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=168          # hours — one week
- EXECUTIONS_DATA_PRUNE_MAX_COUNT=5000
- EXECUTIONS_DATA_SAVE_ON_SUCCESS=none # keep failures, drop the noise

EXECUTIONS_DATA_SAVE_ON_SUCCESS=none is the single biggest win on a high-frequency instance: it stops writing the payloads of runs that worked, while still storing every failed run in full so you can debug it. Keep it at "all" while you are still building the workflow, then flip it once the workflow is boring.

Silence. The Compose file already turns off diagnostics, version notifications and the personalisation survey. The remaining outbound caller is the template gallery, which fetches from api.n8n.io; set N8N_TEMPLATES_ENABLED=false if you want no third-party call at all. If you do disable version notifications, put a monthly reminder in your calendar to read the release notes — a self-hosted instance that nobody updates is a worse outcome than one that pings for versions.

The Code node. N8N_BLOCK_ENV_ACCESS_IN_NODE=true, already in the file, stops expressions and Code nodes from reading process environment variables — which on this box means the PostgreSQL password and the encryption key. If you do not use the public REST API, add N8N_PUBLIC_API_DISABLED=true and close that surface too.

Backups — all three parts or none. A backup of the database alone is worthless without the encryption key, and the key alone restores nothing. Back up the PostgreSQL dump, the n8n data volume and the .env together, and keep at least one copy off the server:

cd /opt/n8n
docker compose exec -T postgres pg_dump -U n8n n8n | gzip > backup-db-$(date +%F).sql.gz
docker run --rm -v n8n_n8n_data:/data -v "$PWD":/backup alpine \
  tar czf /backup/backup-vol-$(date +%F).tar.gz -C /data .
cp .env backup-env-$(date +%F)

The volume name is the Compose project name plus the volume name; if your directory is not called n8n, run docker volume ls and use what you see. Put the three lines in a cron job, ship the archives somewhere else, and test a restore once — an untested backup is a belief, not a backup.

Updates. docker compose pull followed by docker compose up -d. Take a snapshot first: n8n runs database migrations on start, and migrations are not designed to be rolled back. Across a major version — the 1.x to 2.x jump, for instance — read the release notes before pulling rather than after, and consider pinning an explicit image tag instead of :latest so that an unattended restart never upgrades you by surprise.

Going further · Scaling

When one process is not enough. Queue mode, Redis and workers.

By default n8n runs in regular mode: the same process that serves the editor and receives webhooks also executes the workflows. It is simple and it is correct until one long workflow starts making the others wait. The symptom is unmistakable — executions sit in "running" for minutes, the editor gets sluggish, and a webhook that should answer in 200 ms answers in eight seconds.

Queue mode splits the job. The main instance keeps the editor, the triggers and the webhook endpoints; it pushes execution IDs into Redis; separate worker processes pull them, load the workflow from PostgreSQL, run it, and report back through Redis. Three rules follow from that architecture and all three bite people who skip them: every instance must share the same PostgreSQL database, every instance must carry the same N8N_ENCRYPTION_KEY, and SQLite is not supported at all.

The additions to the Compose file are a Redis service and one worker service, which is the same n8n image run with the worker command:

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: ["redis-server", "--save", "60", "1", "--appendonly", "no"]
    volumes:
      - redis_data:/data

  n8n-worker:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    command: worker --concurrency=5
    depends_on:
      - redis
      - postgres
    environment:
      # the SAME encryption key and the SAME database as the main instance
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${GENERIC_TIMEZONE}

Add EXECUTIONS_MODE=queue and QUEUE_BULL_REDIS_HOST=redis to the main n8n service as well — both sides need to agree on the mode. Two options are worth setting on day one: OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true, so that pressing "Test workflow" in the editor does not tie up the main process, and N8N_GRACEFUL_SHUTDOWN_TIMEOUT, which defaults to 30 seconds and decides how long a worker is allowed to finish its current job during a redeploy. If your workflows routinely run longer than half a minute, raise it or every deployment kills work in flight.

Do not start here. Queue mode adds two moving parts and a class of failure that regular mode simply does not have. Stay in regular mode until you can see the queue growing or one workflow blocking another, then add a single worker on the same box before you add a second box. That progression — Sentinel in regular mode, Garrison with one worker, Ravelin with three — covers everything short of a genuinely large deployment.

The layer under the application

What the machine actually holds. And why that changes who should own it.

Most self-hosting guides treat the choice of host as a performance question. For an automation engine it is not. An n8n instance holds two things that almost nothing else you self-host holds together: a single encrypted table containing the API keys, OAuth tokens and mail passwords of every service you automate, and — right beside it — a graph that describes exactly how your organisation works. Which CRM. Which bank feed. Which supplier. Which customers get which email, on which trigger. Read the workflow list of a company and you have read the company.

Layer one — who the host thinks you are. The application layer here is genuinely good: credentials are encrypted at rest, the editor is behind TLS and 2FA. The layer that leaks is the one underneath. A hosted plan knows your legal entity, your billing address and your card. A hyperscaler knows the same and keeps it for years. That is not a hypothetical exposure; it is the join key between "an encrypted vault exists" and "it belongs to this named company". A signup with no email and no identity document, settled in Monero, removes the join key rather than the vault.

Layer two — the disk. Encryption at rest only helps against someone who does not also have the key, and on a default install the key sits on the same filesystem as the database. Keep the .env at mode 600, keep a copy of the key off the machine, and prefer a provider whose jurisdiction does not make a datacentre a convenient place to serve process — which is the entire argument of the Nordic jurisdictions guide.

Layer three — the exit IP. Every HTTP Request node leaves from the VPS address, and that address carries a reputation. Hyperscaler ranges are the most aggressively rate-limited and CAPTCHA-walled on the internet, because that is where the scrapers live; a workflow that scrapes or polls will start failing on an AWS or DigitalOcean IP long before it fails on a quieter Nordic range. n8n also honours the standard HTTP_PROXY, HTTPS_PROXY, ALL_PROXY and NO_PROXY variables, so the handful of workflows that need a different exit can be routed through a local SOCKS proxy or Tor while everything else goes direct.

One more door, new in the 2.x line: n8n can expose an instance-level MCP server so an AI agent can call your workflows as tools. It is genuinely useful and it is also a public endpoint into your automation layer, which deserves the same treatment as any other — see the remote MCP server guide for the TLS, OAuth and exposure reasoning.

Field notes · Six traps

Six ways this goes wrong. In the order people hit them.

Trap 01 · Irreversible

Every credential suddenly fails to decrypt

The data volume was recreated and n8n generated a fresh encryption key. Nothing recovers the old credentials. Always set N8N_ENCRYPTION_KEY explicitly and keep a copy off the server.

Trap 02 · Integration

The webhook URL says localhost

WEBHOOK_URL is unset, so n8n builds URLs from N8N_HOST. Set WEBHOOK_URL and N8N_EDITOR_BASE_URL to the public HTTPS address and restart the container.

Trap 03 · Access

The login form loops forever

You are reaching the editor over plain http and the secure session cookie is refused. Finish the TLS setup rather than setting N8N_SECURE_COOKIE to false on a public instance.

Trap 04 · Capacity

The disk fills up after two months

Fourteen days of full execution data from a minute-by-minute trigger. Tighten EXECUTIONS_DATA_MAX_AGE and PRUNE_MAX_COUNT, and stop saving successful runs.

Trap 05 · Upgrade

An unattended restart pulled a major version

The :latest tag plus database migrations that do not roll back. Pin an explicit tag, snapshot before every pull, and read the release notes across major versions.

Trap 06 · Scheduling

Schedule triggers fire at the wrong hour

GENERIC_TIMEZONE defaults to America/New_York, which is rarely what anyone wants. Set GENERIC_TIMEZONE and TZ to the same real zone and restart.

FAQ · Self-host n8n

Questions, answered.

Ten questions that come up before, during and after moving an n8n instance onto your own server.

Is self-hosting n8n really free?

For your own automations, yes. n8n ships under the Sustainable Use License: you may use, copy, modify and distribute it for internal business purposes and for personal or non-commercial use, at no cost. What the licence forbids is charging others for n8n or a derivative of it — in practice, reselling "n8n hosting" as a product. Files with .ee. in the filename or .ee in the directory path are carved out of that licence and require a paid n8n Enterprise License. So: automating your own company on a VPS you rent is squarely inside the free grant; building a hosted n8n business on top of it is not.

How much VPS do I need to run n8n?

n8n's own Docker Compose documentation states a minimum of 2 vCPU and 4 GB of RAM. That is exactly the Sentinel tier ($3.90/mo — 2 vCPU, 4 GB, 120 GB NVMe), which comfortably runs n8n plus PostgreSQL plus Caddy for a personal or small-team instance. Move up to the Garrison (4 vCPU, 8 GB, $7.90/mo) when you add queue-mode workers or run workflows that hold large payloads in memory, and to the Ravelin (8 vCPU, 16 GB, $16.90/mo) for a team instance doing thousands of executions a day with binary data — PDFs, images, audio.

SQLite or PostgreSQL for n8n?

SQLite is the default and it is genuinely fine for one person with a handful of workflows. Switch to PostgreSQL when you have concurrent executions, when the execution history grows past a few hundred thousand rows, or when you plan to scale — and note that queue mode does not support SQLite at all. Migrating later means exporting workflows and credentials and re-importing them into a fresh instance, which is an afternoon you will not enjoy. If there is any chance of growth, start on PostgreSQL; the Compose file in this guide already does.

What happens if I lose the N8N_ENCRYPTION_KEY?

Every credential in the database becomes permanently unreadable. n8n encrypts stored credentials — OAuth tokens, API keys, SMTP passwords — with that key, and there is no recovery mechanism and no support ticket that will get them back. You re-enter each credential by hand. This is the single most common way a self-hosted n8n instance is destroyed: someone recreates the Docker volume, n8n generates a fresh key, and every workflow starts failing at once with a decryption error. Set the key explicitly in your .env before the first boot, and store a copy somewhere that is not the server.

Why are my n8n webhooks not firing?

Four causes, in order of frequency. (1) WEBHOOK_URL is unset, so the editor hands you a http://localhost:5678/webhook/… URL that no external service can reach — set it to your public HTTPS URL. (2) The workflow is not activated; the test URL only listens while the editor is open, the production URL only exists once the workflow is active. (3) DNS or the firewall: the record does not resolve, or ports 80/443 are closed. (4) You are behind an extra proxy layer and did not set N8N_PROXY_HOPS, so n8n reads the wrong client IP. Test with a plain curl from a machine that is not the server.

Can I run n8n alongside other services on the same VPS?

Yes, and it is the normal pattern — one Caddy in front, one Compose network, n8n on one hostname and Vaultwarden, Nextcloud or SearXNG on others. Two caveats. Memory: n8n plus PostgreSQL idles around 700 MB and a heavy workflow can spike well past that, so leave headroom. Blast radius: the n8n database is the most credential-dense thing on the box, so anything else sharing that host inherits its risk profile. On a $3.90/month tier it is reasonable to give the automation engine its own server.

Does self-hosted n8n phone home?

By default, three endpoints. Anonymous product telemetry (N8N_DIAGNOSTICS_ENABLED, default true), the new-version and security-update check against api.n8n.io (N8N_VERSION_NOTIFICATIONS_ENABLED, default true), and the workflow template browser, which fetches from https://api.n8n.io (N8N_TEMPLATES_ENABLED, default true). None of them ship your credentials or your workflow data, but all three announce that an instance exists at your IP. Set all three to false if you want the box to be silent — you lose the template gallery and the update banner, so keep an eye on releases yourself.

n8n Cloud or self-hosted — where is the break-even?

n8n Cloud Starter is €20/month billed annually for 2,500 executions; Pro is €50/month for 10,000. A Sentinel VPS is $3.90/month and the execution count is bounded only by CPU and RAM, which for typical webhook-and-API workflows means tens of thousands. The money break-even is immediate; the real cost is operational. Self-hosting means you own the upgrades, the backups, the TLS renewal and the 3 a.m. disk-full incident. The honest rule: if you would not have upgraded the instance yourself within a month of a security release, pay for Cloud. If a monthly docker compose pull is already part of your life, self-host.

Why does a KYC-free host matter for an automation server specifically?

Because of what an n8n instance holds. The credential table is a single encrypted store of the API keys, OAuth tokens and mail passwords for every service you automate, and the workflow graph beside it is a readable map of how your business actually operates — which CRM, which bank feed, which supplier, which customers. A static website leaks none of that. The application layer protects it well; the metadata layer is where it leaks. If the signup for the machine carries a passport scan and a card, you have encrypted the vault and written your name on the door. A no-KYC host paid in Monero keeps the two layers aligned.

Can I run AI agents in n8n on this VPS?

Yes for the common shape — an AI Agent node calling a remote model API. That workload is I/O-bound, it waits on the provider, and a Sentinel handles it. What does not fit is running the model itself: a 7B local model wants around 8 GB of RAM and real inference speed wants a GPU, which these tiers do not carry. Point n8n at a remote OpenAI-compatible endpoint and keep the VPS light. The companion guide on running an AI agent 24/7 covers the runtime side — restart policies, secrets, spend caps and the crash-loop trap.

Get the metal

A Nordic VPS for your automation engine. KYC-free, crypto-paid.

Sentinel (2 vCPU, 4 GB, 120 GB NVMe, $3.90/mo) meets the n8n minimum with room for PostgreSQL and Caddy on the same box. No email at signup, no identity document, unlimited executions.

Last reviewed · 2026-08-24 · Sources · n8n hosting documentation, n8n LICENSE.md (Sustainable Use License), n8n.io pricing page, Docker and Caddy upstream docs · Cadence · yearly