Securing a self‑hosted n8n instance demands layered hardening across six
dimensions: authentication, code execution isolation, file‑access
restrictions, network‑layer controls, container‑level configurations, and
audit monitoring. n8n 2.0 locked down defaults that were previously
permissive—Code nodes can no longer access process.env,
ExecuteCommand and LocalFileTrigger nodes are disabled by default, and
Task Runners now provide process‑level isolation for code execution
[1].
This guide provides the definitive hardening blueprint used by
production teams.
| Dimension | Key Environment Variables | What It Protects | Since |
|---|---|---|---|
| Authentication | N8N_BASIC_AUTH_ACTIVE, N8N_BASIC_AUTH_USER, N8N_BASIC_AUTH_PASSWORD |
Editor UI + REST API | v0.x |
| Code Isolation | N8N_BLOCK_ENV_ACCESS_IN_NODE, NODES_EXCLUDE, N8N_RUNNERS_ENABLED |
Prevents RCE, credential leaks, file access | v2.0 |
| File Access | N8N_RESTRICT_FILE_ACCESS_TO, N8N_BLOCK_FILE_ACCESS_TO_N8N_FILES |
Prevents arbitrary file read/write on host | v2.0 |
| Network | N8N_PROXY_HOPS, N8N_HOST, N8N_PROTOCOL |
HTTPS enforcement, reverse proxy trust, correct webhook URLs | v0.x |
| Container | N8N_PAYLOAD_SIZE_MAX, pinned image tags, read_only rootfs |
DoS protection, reproducible deployments, immutable containers | v0.x |
| Audit | EXECUTIONS_DATA_SAVE_ON_SUCCESS, N8N_LOG_LEVEL |
Execution history, debug logs for incident investigation | v0.x |
How do you enable Basic Auth as the minimum authentication layer for n8n?
Set three environment variables: N8N_BASIC_AUTH_ACTIVE=true,
N8N_BASIC_AUTH_USER=admin, and
N8N_BASIC_AUTH_PASSWORD=your-secure-password. After restart,
any request to the n8n editor UI or REST API prompts a browser
authentication dialog. This is the recommended minimum security baseline
before exposing n8n to the internet, and it is lightweight enough that it
does not interfere with workflows or webhook operations.
[5]
Basic Auth protects the editor and API only—it does not
secure webhook endpoints. Webhooks stay public so external services
(Stripe, Slack, GitHub) can call them; you must secure webhooks separately
using HMAC signatures, API keys, or custom validation logic inside the
workflow [5]. Always
combine Basic Auth with HTTPS (via Nginx or Caddy) because credentials
are transmitted with every request. For Docker deployments, these
variables go in the .env file or directly in the
docker-compose.yml. Never commit Basic Auth credentials to
git; store them in a secrets manager or environment file excluded from
version control. For the complete webhook security hardening guide,
see the
n8n Webhook Node Security guide.
How do Task Runners and NODES_EXCLUDE prevent Code‑node sandbox escapes?
Task Runners provide process‑level isolation for Code
nodes. In internal mode (default), the Task Runner runs
as a separate Node.js process spawned by the main n8n process—providing
some isolation but still on the same host. In external mode,
the Task Runner runs in a dedicated sidecar container using the separate
n8nio/runners Docker image, completely isolated from the main
n8n process [6].
For defense‑in‑depth, combine Task Runners with
NODES_EXCLUDE to block high‑risk nodes entirely. The
critical configuration pattern:
NODES_EXCLUDE=["n8n-nodes-base.executeCommand","n8n-nodes-base.localFileTrigger","n8n-nodes-base.code"]
disables shell execution, arbitrary file access, and the Code node
respectively. If you must keep Code nodes enabled, at minimum
set N8N_RUNNERS_ENABLED=true with
N8N_RUNNERS_MODE=external so Python/JavaScript code executes
in the isolated sidecar [6]. Four
2026 CVEs exploited Pyodide or internal‑mode sandbox escapes to achieve
remote code execution; all were mitigated by upgrading and switching to
external Task Runners. For the complete CVE mitigation
strategy, see the
Webhook Node Security guide.
n8n-nodes-base.code to
NODES_EXCLUDE. (2) Must you run Code nodes? → Enable Task
Runners in external mode: N8N_RUNNERS_ENABLED=true,
N8N_RUNNERS_MODE=external, and deploy the
n8nio/runners sidecar image. (3) Also set
N8N_BLOCK_ENV_ACCESS_IN_NODE=true to prevent credential
leakage through process.env [1].
How do you restrict file access and environment variable exposure in Code nodes?
n8n 2.0 locked down file access by default: workflows can only access
~/.n8n-files unless N8N_RESTRICT_FILE_ACCESS_TO
explicitly allows another directory. Set this to a dedicated, empty
directory containing no sensitive data. Keep
N8N_BLOCK_FILE_ACCESS_TO_N8N_FILES=true (the default) to
block Code nodes from reading the .n8n config directory
where encryption keys and credentials are stored [7].
Setting N8N_BLOCK_ENV_ACCESS_IN_NODE=true (default in v2.0)
prevents Code nodes from accessing process.env—a critical
security win that stops workflows from reading database passwords, SMTP
credentials, encryption keys, and cloud provider keys [4]. If a
workflow genuinely needs an environment variable value, pass it
explicitly as an input parameter: add a Set node before the Code node
that reads $env:MY_API_KEY and passes it as a JSON field.
This pattern keeps control in your hands and never exposes environment
variables to the Code node. For database password protection, use
the _FILE suffix notation—Docker Compose mounts a
secrets file, and n8n reads the password from the file rather than
from an environment variable [8]. For the
complete environment variable and secrets management guide, see the
n8n Credential Nodes guide.
How do you configure Nginx or Caddy as a reverse proxy for HTTPS and secure WebSocket connections?
A reverse proxy sits between the internet and n8n, terminating HTTPS
and forwarding traffic to localhost:5678. For production
with Nginx, configure a server block listening on port 443,
proxy to http://n8n:5678, and forward four critical headers:
Host $host, X-Forwarded-Proto $scheme,
X-Forwarded-For $proxy_add_x_forwarded_for, and
X-Real-IP $remote_addr [9].
For WebSocket support (required for the n8n editor live‑reload
and real‑time execution monitoring), add
proxy_set_header Upgrade $http_upgrade; and
proxy_set_header Connection "upgrade";. Set
proxy_read_timeout 3600s; to prevent timeouts during
long‑running workflow executions. Then set
N8N_PROXY_HOPS=1 so n8n trusts the proxy headers and
correctly constructs webhook URLs with https://. Without
this setting, webhook URLs may use http:// and break
integrations [1]. For
teams who prefer simpler TLS setup, Caddy auto‑negotiates HTTPS with
zero configuration beyond reverse_proxy n8n:5678 and
automatically handles certificate renewal [10].
For the complete reverse‑proxy configuration blueprint including
DDoS protection headers and rate limiting, see the
n8n Docker Compose production stack guide.
proxy_set_header Host $host; — corrects webhook origin
URLs. proxy_set_header X-Forwarded-Proto $scheme; —
ensures HTTPS appears in generated links. proxy_set_header
X-Forwarded-For $proxy_add_x_forwarded_for; — preserves real
client IP for IP allowlisting. proxy_set_header Upgrade
$http_upgrade; + proxy_set_header Connection
"upgrade"; — enables WebSocket for editor live‑reload
[9].
How do you configure host‑level and container‑level firewall rules for n8n?
Use UFW (Uncomplicated Firewall) at the host level:
ufw allow 443/tcp && ufw allow 80/tcp && ufw enable.
This opens only HTTPS and HTTP ports—port 5678 must remain closed to
external traffic. Bind n8n to the loopback interface with
N8N_HOST=127.0.0.1 so n8n only listens on localhost,
forcing all external traffic through the reverse proxy
[1].
At the container level, enforce egress filtering
with iptables or nftables: restrict outbound
traffic from the n8n container to only the specific IPs or domains
your workflows need. This prevents a compromised workflow from
exfiltrating data to arbitrary external servers
[11]. The
strictest approach blocks all egress by default, then whitelists
only approved destinations (Slack webhook URLs, Stripe API endpoints,
your database server). For Docker deployments, define the
n8n-network as an isolated bridge network with
internal: true so only explicitly defined services can
communicate, and use service‑level restart: unless-stopped
with health checks on all containers. For additional logging, use
Docker’s --log-driver=json-file with
--log-opt max-size=10m and max-file=3 to
prevent disk exhaustion from debug logs [12]. For
the production‑ready Docker Compose blueprint that combines all
these controls, see the
n8n Docker Compose production stack guide.
| Layer | Tool / Setting | Command / Value | What It Blocks |
|---|---|---|---|
| 1. Host Ingress | UFW | ufw allow 443/tcp && ufw allow 80/tcp |
Direct access to port 5678, SSH brute‑force |
| 2. Service Bind | Environment | N8N_HOST=127.0.0.1:5678 |
Bypass of reverse proxy via direct port access |
| 3. Container Egress | iptables / Docker network | internal: true on Docker network |
Data exfiltration to arbitrary external servers |
| 4. Reverse Proxy Auth | Nginx / Caddy | TLS termination + header forwarding | MITM attacks, incorrect webhook URL generation |
How do you audit your self‑hosted n8n instance and monitor for security incidents?
Begin with a workflow content audit: check every Code
node for references to process.env, hardcoded API keys,
passwords, tokens, or secrets—these should immediately be replaced
by credential nodes or input‑parameter passing. Review each HTTP
Request node to verify it only calls expected endpoints, and scan
for ExecuteCommand or LocalFileTrigger nodes unless they are
explicitly required [3].
For operational monitoring, set
EXECUTIONS_DATA_SAVE_ON_SUCCESS=all to retain input/output
data for successful runs (not just failures), and
N8N_LOG_LEVEL=debug for detailed logs [13].
Build a monitoring workflow that queries the n8n REST API for
execution history, filters for anomalies (unexpected HTTP status
codes, long execution durations, access from unusual IPs), and posts
alerts to a dedicated security Slack channel. Combine these logs
with an external log aggregator (Loki, ELK, or Datadog) for
cross‑instance visibility and real‑time alerting. For compliance
audits, maintain the instance at a known version (pin Docker image
tags), regularly apply security patches, and follow the WotAI audit
checklist—the same checklist used internally by professional
automation agencies [3]. For
integrating security monitoring with incident response pipelines,
see the
n8n DevOps Nodes guide.
References
- n8n Documentation — Security Environment Variables: N8N_BLOCK_ENV_ACCESS_IN_NODE, NODES_EXCLUDE, N8N_RESTRICT_FILE_ACCESS_TO, N8N_PROXY_HOPS, N8N_HOST
- NIST NVD — CVE‑2026‑27494: n8n Arbitrary File Read via Python Code Node Sandbox Escape, CVSS 9.9 CRITICAL, fixed in 2.10.1/2.9.3/1.123.22
- WotAI — How to Audit Your n8n Workflows for Security Vulnerabilities: Code node review, HTTP endpoint audit, workflow permissions check (Apr 2026)
- WotAI — n8n v2.0 Migration Guide: environment variables in Code nodes blocked, ExecuteCommand/LocalFileTrigger disabled, Task Runners default (Dec 2025)
- RapidDevelopers — How to Secure n8n with Basic Auth: N8N_BASIC_AUTH_ACTIVE, Docker Compose setup, webhook exemption, HTTPS requirement
- n8n Documentation — Task Runners: internal vs external mode, n8nio/runners sidecar image, N8N_RUNNERS_ENABLED configuration
- NIST NVD — CVE‑2025‑68697: n8n File Access via Code Node in Legacy JavaScript Mode, CVSS 5.4 MEDIUM, fixed in 2.0.0
- n8n Community — How to Protect the Secrets of n8n Itself: N8N_BLOCK_ENV_ACCESS_IN_NODE, NODES_EXCLUDE, _FILE suffix for secrets (Apr 2024)
- dev.to — How to Set Up Nginx Reverse Proxy with SSL (Let’s Encrypt): security headers, rate limiting, hardening config (Apr 2026)
- Tsmx — Hardening n8n Docker: reverse proxy with Caddy, firewall with ufw, persistent volumes, HTTPS enforcement (2025)
- n8n Community — Deploy Instance with No Outbound Traffic: iptables/nftables egress filtering, OS‑level firewall, Docker network isolation (Jul 2025)
- n8n Documentation — Docker Installation: persistent volumes, restart policies, health checks, log driver configuration, Docker Compose example
- n8n Documentation — Execution Data: EXECUTIONS_DATA_SAVE_ON_SUCCESS, EXECUTIONS_DATA_MAX_AGE, pruning configuration for audit trails
- n8n Documentation — Blocking Nodes: NODES_EXCLUDE syntax, disabling specific nodes, security implications, recommended exclusions
- Franklinetech — How to Self-Host n8n on a VPS: firewall rules, Nginx reverse proxy, Docker Compose with PostgreSQL, SSL persistence (Apr 2026)

