Skip to content
SelfHostingN8N

Self-hosting

Securing Self-Hosted n8n: A Practical Hardening Checklist

Harden your self-hosted n8n step by step: SSH and firewall basics, 2FA, locking down the editor, webhook security, and the built-in security audit.

Think about what your n8n instance actually holds: API keys for your email, your database passwords, OAuth tokens for your company’s Slack, maybe payment provider credentials. An attacker who owns your n8n doesn’t just own a workflow tool — they own everything it’s connected to, plus a server that executes arbitrary code on demand.

That’s the sober framing for this guide. The good news: securing a self-hosted n8n is not exotic. It’s a stack of standard, boring measures, each cheap on its own, that together make you a much harder target than the thousands of exposed instances that show up on Shodan every month.

Work through this top to bottom. Items are ordered by impact-per-minute.

1 — Close the server’s doors

If you followed our VPS guide, you already did the two highest-impact items. Verify them anyway:

Firewall: three ports, nothing else.

sudo ufw status verbose
# should allow: OpenSSH (22), 80/tcp, 443/tcp — and nothing more

Critically, n8n’s port 5678 must not appear here. It stays on the internal Docker network with only Caddy able to reach it. If you can open http://YOUR_IP:5678 from your laptop, fix that before reading further — you’re serving your login page (and every webhook) unencrypted to the internet.

SSH: keys only, no root login. In /etc/ssh/sshd_config:

PasswordAuthentication no
PermitRootLogin no

Then sudo systemctl restart ssh — with a second terminal already logged in, in case of typos. Add fail2ban (sudo apt install fail2ban) to auto-ban brute-forcers; the default config protects SSH out of the box.

Patches, automatically.

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Security patches now land without your attention. n8n itself you update deliberately — that routine is here — because staying current is a security control: workflow tools are high-value targets and fixes ship in ordinary releases.

2 — Lock the front door (the editor)

With the network tight, the editor login is your public attack surface.

Turn on two-factor auth. Settings → Personal → Two-factor authentication, scan the QR into any TOTP app, store the recovery codes in your password manager. Do this for every user, starting with the owner. This single toggle defeats credential-stuffing, which is the attack your instance will actually face.

Use a real password. The owner account gates every credential in the database. Password-manager-generated, unique, done.

Optional: hide the editor entirely. If only you (or a small team) ever open the UI, don’t serve it to the whole internet. Two clean options in the Caddyfile:

IP allowlist — editor for your IPs, webhooks for everyone (external services must still reach /webhook/):

n8n.yourdomain.com {
    @editor {
        not path /webhook/* /webhook-test/* /rest/oauth2-credential/*
        not remote_ip 203.0.113.7   # your IP / office / VPN
    }
    respond @editor 403
    reverse_proxy n8n:5678
}

Or basic auth in front of the UI — same @editor matcher with basic_auth instead of respond 403 gives you a second password layer instead of an IP dependency. If you have a VPN like Tailscale or WireGuard, better still: bind the editor to it and expose only webhooks publicly.

3 — Harden the n8n application itself

A handful of environment variables and settings tighten the app layer (add to .env / Compose — full reference in our env vars guide):

# Refuse to start without HTTPS-only auth cookies (default true — don't disable it
# to "fix" a proxy problem; fix the proxy, see the troubleshooting guide).
N8N_SECURE_COOKIE=true

# If you don't use the REST API programmatically, remove that surface entirely.
N8N_PUBLIC_API_DISABLED=true

# Block workflows (and anyone who compromises one) from reading the
# instance's own environment variables — where your DB password lives.
N8N_BLOCK_ENV_ACCESS_IN_NODE=true

# Auto-log-out idle editor sessions.
N8N_USER_MANAGEMENT_JWT_DURATION_HOURS=24

And the one you already know but it belongs on the checklist: N8N_ENCRYPTION_KEY set explicitly, backed up in a password manager. It’s what makes a stolen database dump merely bad instead of catastrophic — credentials at rest are encrypted with it. (Backup guide.)

Run n8n’s built-in security audit. n8n ships a self-assessment that flags risky credentials, unprotected webhooks, and unsafe instance settings:

docker compose exec n8n n8n audit

Run it after finishing this checklist and again every few months. It’s the closest thing to a free pentest-lite you’ll get.

4 — Webhooks: authenticated, not obscure

Every active webhook workflow is an unauthenticated public endpoint unless you make it otherwise. The URL being “hard to guess” is not security — URLs leak (logs, referrers, screenshots).

  • In the Webhook node → Authentication, require a Header Auth token (or basic auth) and configure the calling service to send it. Anything arriving without the secret gets rejected before your workflow runs.
  • For providers that sign payloads (Stripe, GitHub, Slack…), verify the signature — dedicated trigger nodes mostly handle this for you; raw Webhook nodes need a verification step.
  • Treat webhook payloads as untrusted input, because that’s what they are. Validate before acting — especially before anything that touches a database, runs a Code node on the data, or feeds an LLM in an AI workflow.

5 — Blast-radius thinking

Assume something eventually goes wrong; arrange for it to matter less.

  • Least-privilege credentials. The API key you store in n8n should do only what the workflow needs. Read-only scopes where possible, a dedicated DB user with grants on specific tables — not postgres superuser, not your personal admin token.
  • One instance per trust level. Experiments and production automations shouldn’t share a box. A €4 second VPS is cheap isolation.
  • Multiple users, real roles. If teammates use the instance, everyone gets their own account (with 2FA) — shared logins destroy your audit trail. Keep the owner account for administration only.
  • Tested backups. Security incidents end in one of two ways: with a restore, or with a rebuild. Make sure you’re in the first group.

The checklist

  • UFW: 22/80/443 only; port 5678 unpublished (Docker ports: on Caddy only)
  • SSH: key-only auth, no root login, fail2ban running
  • unattended-upgrades on; n8n updated on a schedule
  • 2FA on every account; recovery codes stored
  • Editor hidden behind IP allowlist / basic auth / VPN (if feasible)
  • N8N_ENCRYPTION_KEY explicit and backed up
  • Public API disabled if unused; env access blocked in nodes
  • Every webhook authenticated; signatures verified where offered
  • Least-privilege credentials throughout
  • n8n audit run — and its findings actually addressed

None of this took specialist knowledge — just the discipline to do it once. If self-hosting’s security ownership is the part that gives you pause, that’s a fair signal to weigh managed options honestly instead of running an instance you won’t maintain.