You followed our self-hosted n8n Docker guide, your workflows are running, and data is flowing between your services. Now the question is: how exposed is this thing?
The default n8n installation is designed for convenience, not security. That's fine for local development. It is not fine for a production instance connected to your Stripe account, your CRM, your email provider, and your production database. This checklist covers every layer — from the operating system up through n8n's own configuration — so you can harden your instance methodically rather than discovering gaps after an incident.
1. Why n8n Security Matters
n8n is a credential aggregator by design. Every workflow that connects to an external service stores authentication material: API keys, OAuth tokens, database connection strings, SMTP credentials, webhook secrets. A single n8n instance running 20 workflows might hold credentials for Stripe, HubSpot, PostgreSQL, SendGrid, Slack, Google Sheets, and a dozen other services. Compromise the n8n instance and you have the keys to all of them.
This is not theoretical. A publicly exposed n8n instance with default settings — no authentication, no encryption key, port 5678 open to the internet — is a treasure trove for anyone who finds it. Shodan and similar scanning tools index exposed n8n instances regularly. Even with basic authentication enabled, a weak password and no rate limiting means brute-force attacks are trivial.
The attack surface extends beyond the editor UI. n8n exposes webhook endpoints that accept inbound HTTP requests. If your workflows process webhook data without validation, an attacker can trigger workflow executions with crafted payloads. Execution logs may contain sensitive data from previous runs — API responses, customer records, financial transactions — and these persist in the database unless you configure retention limits.
The hardening steps below address each of these vectors. None of them are optional for a production deployment.
2. Server-Level Hardening
Before touching n8n's configuration, lock down the server itself. These steps apply to any Ubuntu/Debian VPS, whether you're running n8n or anything else.
Configure the firewall
UFW (Uncomplicated Firewall) ships with Ubuntu. Enable it with a default-deny policy and only allow the ports you need:
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH, HTTP, and HTTPS
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable the firewall
sudo ufw enable
# Verify rules
sudo ufw status verbose
That's it. Port 5678 (n8n's default) is intentionally not opened — n8n will sit behind a reverse proxy on ports 80/443. Never expose 5678 directly to the internet.
Install Fail2ban for SSH protection
Fail2ban monitors log files and bans IPs that show malicious patterns. For SSH brute-force protection:
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
The default configuration bans an IP for 10 minutes after 5 failed SSH attempts. For a production server, create a stricter local override:
sudo cat > /etc/fail2ban/jail.local <<EOF
[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 3
bantime = 3600
findtime = 600
EOF
sudo systemctl restart fail2ban
This bans IPs for one hour after 3 failed attempts within a 10-minute window.
Disable root login and require SSH keys
# Generate SSH key on your local machine (if you haven't already)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Copy public key to server
ssh-copy-id user@your-server-ip
# Then on the server, edit SSH config
sudo nano /etc/ssh/sshd_config
Set the following directives in /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
Restart the SSH daemon:
sudo systemctl restart sshd
Enable automatic security updates
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades
This ensures critical security patches are applied automatically without manual intervention. Kernel updates still require a reboot, but application-level patches (OpenSSL, systemd, etc.) apply immediately.
3. n8n-Specific Security Configuration
With the server locked down, configure n8n itself. These settings go in your .env file or docker-compose.yml environment block.
Set the encryption key
This is the single most important security configuration in n8n. Generate a strong key:
openssl rand -hex 32
Add it to your environment:
N8N_ENCRYPTION_KEY=your_generated_64_character_hex_string
This key encrypts every credential in your database. Lose it = permanently lose access to all stored API keys, OAuth tokens, and database passwords. There is no recovery mechanism. Back up this key separately from your n8n database, store it in a password manager, and document it in your team's secrets vault.
If you deployed n8n without setting this key, n8n used a default key. That means your credentials are encrypted with a publicly known value — functionally equivalent to no encryption at all. Set a proper key, then re-enter all your credentials through the n8n UI so they're re-encrypted with the new key.
Disable public registration
After creating your first admin account, prevent anyone else from registering:
N8N_USER_MANAGEMENT_DISABLED=false
N8N_PUBLIC_API_DISABLED=false
In the n8n Settings UI, navigate to Users and ensure that sign-up is disabled. Only invite new users explicitly through the admin panel. For single-user instances, this prevents an attacker from creating their own account on an exposed instance.
Enforce HTTPS on webhooks
The WEBHOOK_URL must use HTTPS. This isn't just about encrypting data in transit — many external services (Stripe, GitHub, Slack) refuse to send webhooks to HTTP endpoints:
WEBHOOK_URL=https://n8n.yourdomain.com/
If WEBHOOK_URL is not set, n8n generates webhook URLs based on the hostname it detects. Behind a reverse proxy, this often results in http://localhost:5678 URLs that external services can't reach. Always set it explicitly.
Configure session timeout
By default, n8n sessions persist for a long time. For production instances — especially those accessible over the internet — reduce the session duration:
N8N_USER_MANAGEMENT_JWT_DURATION_HOURS=2
This forces re-authentication every 2 hours. Adjust based on your workflow — a team that's actively building automations might prefer 8 hours; a high-security environment should use 1-2 hours.
Prune execution data
Execution logs contain the input and output of every node in every workflow run. That includes API responses, customer data, and anything else your workflows process. Limit how long this data persists:
EXECUTIONS_DATA_MAX_AGE=168
EXECUTIONS_DATA_PRUNE=true
This prunes execution data older than 168 hours (7 days). For workflows that process sensitive data — payment information, personal records, health data — consider a shorter retention window of 24-48 hours. You need enough history to debug failed workflows, but not enough to become a liability if the database is compromised.
Disable telemetry
n8n collects anonymized usage data by default. In regulated environments or when handling sensitive data, disable it:
N8N_DIAGNOSTICS_ENABLED=false
This prevents any data from being sent to n8n's telemetry servers. It has no impact on functionality.
4. Network Security
n8n's application-level settings protect the software. Network-level configuration protects the server from threats that never reach n8n's code.
Never expose port 5678 directly
n8n listens on port 5678 by default. This port should never be reachable from the public internet. All traffic should flow through a reverse proxy (Caddy, Nginx, or Traefik) that terminates TLS on ports 80/443 and forwards requests internally to 5678.
If you followed our self-hosting guide, Caddy handles this automatically. Verify that port 5678 is not exposed by checking your Docker Compose file — the n8n service should not have a ports directive mapping 5678 to the host. Only the reverse proxy needs published ports:
# Correct: only Caddy exposes ports
caddy:
ports:
- "80:80"
- "443:443"
# n8n has NO ports section — only reachable via internal Docker network
n8n:
expose:
- "5678"
Test from an external machine: curl -v http://YOUR_SERVER_IP:5678 should time out or return connection refused. If it returns an n8n response, your firewall or Docker network configuration is leaking.
IP whitelisting for the editor
The n8n editor UI is the most sensitive attack surface — it provides full access to create, modify, and delete workflows, and to view stored credentials. Restrict editor access to known IP addresses using your reverse proxy.
In a Caddyfile, use the remote_ip matcher:
n8n.yourdomain.com {
@blocked not remote_ip 203.0.113.10 198.51.100.0/24
respond @blocked 403
reverse_proxy n8n:5678
}
This allows access only from the specified IP address and CIDR range, returning 403 Forbidden to everyone else. Webhook endpoints still need to be publicly reachable, so a more targeted approach whitelists the editor paths while leaving webhook paths open:
n8n.yourdomain.com {
# Webhook endpoints — open to the internet
handle /webhook/* {
reverse_proxy n8n:5678
}
handle /webhook-test/* {
reverse_proxy n8n:5678
}
# Everything else (editor, API) — restricted by IP
handle {
@blocked not remote_ip 203.0.113.10
respond @blocked 403
reverse_proxy n8n:5678
}
}
VPN for admin access
For teams or remote administrators, a VPN tunnel is more robust than IP whitelisting. Tools like WireGuard (lightweight, fast, built into the Linux kernel) or Tailscale (zero-config WireGuard overlay) let you place the n8n editor behind a private network. Admin traffic flows over the encrypted tunnel; webhook traffic flows over the public internet to the reverse proxy. This eliminates the need to update IP whitelists when team members change locations.
Rate limiting on webhook endpoints
Webhook URLs are public by design. Without rate limiting, an attacker who discovers a webhook URL can flood it with requests, consuming n8n execution resources and potentially triggering expensive downstream API calls.
Caddy's rate_limit directive (available via plugin) or an upstream Cloudflare proxy can enforce per-IP rate limits. A reasonable starting point: 60 requests per minute per IP on webhook paths. Legitimate webhook senders (Stripe, GitHub, Shopify) never approach this rate from a single IP.
5. Credential Management
n8n stores credentials for every connected service. How you manage these credentials determines your blast radius if something goes wrong.
Prefer OAuth over API keys
Where the service supports it, use OAuth2 authentication instead of static API keys. OAuth tokens are scoped (you can grant only the permissions n8n needs), time-limited (tokens expire and refresh automatically), and revocable (disable the OAuth app without rotating all keys). API keys are typically permanent, broadly scoped, and revocation means every integration using that key breaks simultaneously.
Apply least-privilege scopes
When creating API keys or OAuth connections, grant only the permissions each workflow requires. A workflow that reads data from HubSpot does not need write access. A workflow that sends Slack messages does not need channel management permissions. If a credential is compromised, least-privilege scoping limits what an attacker can do with it.
Rotate credentials on a schedule
Establish a rotation cadence. For high-sensitivity credentials (payment processors, production databases): rotate every 90 days. For medium-sensitivity (CRM, email providers): rotate every 180 days. After any security incident or team member departure: rotate immediately. n8n makes rotation straightforward — update the credential in the n8n UI and test the affected workflows. Document which workflows use which credentials so rotation does not become a guessing game.
Use environment variables for infrastructure secrets
Database passwords, the encryption key, and Redis credentials should live in a .env file referenced by Docker Compose, not hardcoded in docker-compose.yml:
# .env file (chmod 600, owned by root)
N8N_ENCRYPTION_KEY=your-generated-key-here
POSTGRES_PASSWORD=strong-random-password
REDIS_PASSWORD=another-strong-password
Set file permissions so only root can read the .env file: chmod 600 .env && chown root:root .env. This prevents other users on a shared system from reading your secrets.
6. Monitoring & Logging
Security without monitoring is a locked door with no alarm system. You need to know when something abnormal happens.
Monitor execution patterns
In the n8n UI, review the Executions tab regularly. Look for unusual patterns: workflow executions at unexpected hours, workflows triggered by unknown webhook sources, or a sudden spike in execution volume. These can indicate unauthorized access or webhook endpoint abuse.
Set up external health monitoring
An external monitoring tool like Uptime Kuma (self-hosted, minimal resource footprint) can watch your n8n instance from outside. Configure it to check:
- HTTPS endpoint — checks that the n8n web UI responds on port 443
- Webhook endpoint — sends a test request to a dedicated health-check webhook workflow
- Push monitoring — a scheduled n8n workflow pings Uptime Kuma every 5 minutes. If the ping stops, Uptime Kuma alerts you. This catches application-level failures that HTTP checks miss.
Configure alerts via Slack, email, or Telegram so you are notified within minutes of any failure.
Centralize Docker logs
Docker container logs capture n8n application errors, authentication failures, and webhook processing details. By default, these logs stay on the local filesystem and are lost if the container is recreated. At minimum, configure Docker log rotation (as described in our backup guide) and review logs periodically:
# Check for authentication errors
docker compose logs n8n --since 24h | grep -i "auth\|login\|unauthorized"
# Check for webhook errors
docker compose logs n8n --since 24h | grep -i "webhook\|error"
For production environments, forward logs to a centralized system (Loki, Elasticsearch, or a managed logging service) so they survive container restarts and can be searched historically.
7. Infrastructure-Level Protection
Application hardening protects n8n from software-level threats. Infrastructure hardening protects the server and network from threats that operate below the application layer.
DDoS protection
Webhook endpoints are publicly reachable URLs. A targeted DDoS attack against your webhook URLs can overwhelm your server's network capacity, making the n8n editor inaccessible and causing all webhook deliveries to time out. MassiveGRID provides 12 Tbps DDoS mitigation across all VPS plans — traffic scrubbing happens at the network edge before malicious packets reach your server. Most budget VPS providers offer no DDoS protection or cap it at volumes too low to matter.
Network isolation
Your n8n containers (main process, workers, PostgreSQL, Redis) should communicate over an isolated Docker network that is not bridged to the host's public interface. The default Docker Compose configuration creates this internal network automatically. Verify that your PostgreSQL and Redis containers have no published ports — they should only be reachable by other containers on the same Docker network.
Storage encryption
n8n's N8N_ENCRYPTION_KEY encrypts credentials within the database. But the database files themselves sit on disk. If an attacker gains filesystem access (through a compromised application or stolen backup), unencrypted database files expose execution history, workflow definitions, and metadata. MassiveGRID's Ceph distributed storage encrypts data at rest across all storage nodes, providing an additional layer of protection independent of application-level encryption.
Physical security
This one is easy to overlook because it is out of sight. MassiveGRID's data centers are operated by Equinix and Digital Realty — facilities with biometric access controls, 24/7 security staffing, and CCTV monitoring. This matters for regulated industries (healthcare, finance, government) where physical access controls are part of compliance audits. If your compliance framework requires physical security documentation, ask your VPS provider for their SOC 2 or ISO 27001 certification status.
For personal projects or low-sensitivity automations, server-level hardening (sections 2–3) is sufficient. Infrastructure-level protection becomes genuinely important when your n8n instance handles financial data, personal records, or healthcare information — cases where a breach has legal and financial consequences beyond the immediate technical damage.
Quick Reference Checklist
Copy this checklist and work through it for every production n8n deployment:
| Layer | Item | Command / Setting |
|---|---|---|
| Server | UFW firewall enabled (22, 80, 443 only) | sudo ufw status |
| Server | Fail2ban active for SSH | sudo fail2ban-client status sshd |
| Server | Root login disabled, SSH keys required | /etc/ssh/sshd_config |
| Server | Automatic security updates enabled | unattended-upgrades |
| n8n | Custom encryption key set | N8N_ENCRYPTION_KEY |
| n8n | Public registration disabled | Settings → Users |
| n8n | HTTPS webhook URL configured | WEBHOOK_URL=https://... |
| n8n | Session timeout configured | N8N_USER_MANAGEMENT_JWT_DURATION_HOURS |
| n8n | Execution data pruning enabled | EXECUTIONS_DATA_PRUNE=true |
| n8n | Telemetry disabled | N8N_DIAGNOSTICS_ENABLED=false |
| Network | Port 5678 not exposed publicly | curl YOUR_IP:5678 should fail |
| Network | Editor access restricted (IP or VPN) | Caddyfile / Nginx config |
| Credentials | OAuth used where available | n8n Credentials UI |
| Credentials | Least-privilege scopes applied | Per-service review |
| Credentials | .env file permissions set (600) | ls -la .env |
| Monitoring | External health check configured | Uptime Kuma or equivalent |
None of these steps require significant time or expertise. A fresh n8n deployment can be hardened in under an hour by working through this list top to bottom. The most common mistake is not doing it at all — running a production instance with default settings because the defaults worked during testing.
Next Steps
- Self-host n8n with Docker — the base deployment this checklist builds on
- Backup & disaster recovery — protecting the data that hardening alone cannot save
- Best VPS for n8n — infrastructure choices that affect your security baseline
- Monitor n8n with Uptime Kuma — the external monitoring setup referenced in section 6