n8n self-hosted: when it makes sense and when you are creating a problem
Honest analysis of n8n self-hosted: advantages, risks, security, maintenance, and cases where I would not use it.

I’ve been using n8n self-hosted for internal automations and personal projects for a while now. It’s a tool that has saved me hours of work, but it has also given me a few scares that could have been avoided if I had been more disciplined from the start. What I’m going to share here is not an installation tutorial or a marketing pamphlet. It’s an honest analysis of when running n8n on your own infrastructure is worth it and when you’re buying yourself a problem you don’t need.
The cloud version of n8n exists and works. If the self-hosted option remains popular, it’s for three reasons: full control over data, flexibility for customizations, and no per-execution licensing cost. But each of those advantages comes with a responsibility that many people underestimate.
When self-hosted does make sense
I won’t say “always” because that would be a lie. There are specific scenarios where running n8n on your own server makes perfect sense.
Internal team automations
If your team needs to connect internal tools---databases, Slack, repositories, dashboards---and you don’t want to pay per execution, self-hosted is ideal. Traffic is predictable, users are known, and the risk of uncontrolled scaling is low.
Prototypes and quick validations
When you need to test a new integration or set up a quick flow for a client, having n8n on your local machine or a development server is much more agile than configuring a cloud subscription for an experiment that might last a week.
Small teams with some ops knowledge
If someone on the team knows how to handle Docker, keep a server updated, and configure backups, n8n self-hosted doesn’t require a dedicated infrastructure team. A cheap VPS with Docker Compose is enough for many cases.
Data that cannot leave your network
In regulated industries or projects where data is sensitive, having the automation engine inside your infrastructure can be a requirement, not a preference. Self-hosted gives you the guarantee that nothing leaves your network.
When it does NOT make sense
This is where the conversation gets uncomfortable, because many times the decision to go self-hosted is made out of inertia, perceived savings, or a false sense of control.
When you need high availability and don’t have the infra
If your n8n workflow is critical---for example, it processes payments, sends notifications to customers, or feeds a production system---and your setup is a Docker Compose on a VPS with no redundancy, you’re playing with fire. If the server goes down, workflows are lost. If the disk fills up, n8n stops working without warning.
If your automation is critical for the business and you don’t have the capacity to guarantee availability, the cloud version will probably cost you less than the first outage.
When you handle sensitive data without proper infra
Self-hosted gives you control, but control without discipline is worse than delegating to a competent provider. If you’re going to process personal data, client credentials, or financial information on an n8n instance running on a VPS without HTTPS, without a properly configured firewall, and with default admin credentials, the risk is real.
When nobody on the team wants to maintain it
n8n needs updating. The server needs patching. Backups need to happen. If you set up n8n self-hosted and nobody takes responsibility for maintaining it, in six months you’ll have an outdated version with known vulnerabilities and no backups. I’ve seen it happen.
When the real cost exceeds cloud
Do the honest math. A VPS, the time of a dev maintaining the setup, the risk of downtime, the time debugging when something fails. If the team is three people and the cloud alternative costs 20 euros a month, self-hosted can end up being more expensive in work hours even if the server is cheap.
Docker setup: a realistic starting point
If after evaluating the risks you decide that self-hosted is your path, this is a docker-compose.yml I use as a base. It’s not the minimal example from the documentation; it has the configurations I consider necessary for something that goes beyond an experiment.
# docker-compose.yml
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
# Basic configuration
- N8N_HOST=n8n.tudominio.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.tudominio.com/
# External database (DO NOT use SQLite in production)
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
# Security
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
# Timezone
- GENERIC_TIMEZONE=Europe/Madrid
- TZ=Europe/Madrid
# Encryption key for credentials (IMPORTANT: do not lose this key)
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
networks:
- n8n-network
postgres:
image: postgres:16-alpine
container_name: n8n-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=n8n
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- n8n-network
volumes:
n8n_data:
postgres_data:
networks:
n8n-network:
driver: bridgeAnd the corresponding .env:
# .env
POSTGRES_USER=n8n_user
POSTGRES_PASSWORD=una_password_segura_de_verdad
N8N_USER=admin
N8N_PASSWORD=otra_password_segura
N8N_ENCRYPTION_KEY=genera-una-clave-larga-aleatoria-aquiWhy PostgreSQL and not SQLite
SQLite is n8n’s default and works for testing. But in production it has real problems:
- It doesn’t handle concurrent writes well. If you have multiple workflows running simultaneously, you can get locks.
- Backups are more fragile. Copying an SQLite file while n8n is writing can produce a corrupt backup.
- It doesn’t scale. If your workflows generate high execution volume, SQLite becomes a bottleneck.
PostgreSQL solves all of this and n8n supports it natively.
Security: what you cannot ignore
The security of a self-hosted n8n is not trivial, and it’s probably the aspect most people underestimate. n8n executes code, connects to external services, and stores credentials for APIs, databases, and third-party services. If someone gains access to your instance, they have access to all of that.
HTTPS is mandatory
Don’t expose n8n over HTTP. Ever. Credentials travel in plain text, webhooks are accessible from the internet, and anyone on the same network can intercept the traffic.
The simplest way to add HTTPS is to put a reverse proxy in front. An example with Caddy, which manages certificates automatically:
# Caddyfile
n8n.tudominio.com {
reverse_proxy n8n:5678
}Or with nginx:
server {
listen 443 ssl;
server_name n8n.tudominio.com;
ssl_certificate /etc/letsencrypt/live/n8n.tudominio.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.tudominio.com/privkey.pem;
location / {
proxy_pass http://localhost:5678;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support (required for the n8n editor)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Internal network whenever possible
If n8n is only used by your team, it doesn’t need to be exposed to the internet. Put it on an internal network and access it via VPN. This eliminates most attack vectors in one move.
Authentication
n8n has built-in basic authentication, but it’s not sophisticated. If you need SSO, MFA, or role-based access control, the community self-hosted version falls short. The enterprise version does offer it, but it comes with a cost.
The bare minimum you should configure:
- Basic auth enabled with strong passwords.
- Change the default credentials (seems obvious, but it’s not).
- If you use public webhooks, restrict by IP or add authentication tokens in the workflows themselves.
Backups: what you cannot afford to lose
I’ve seen people lose entire workflows because the server went down and there was no backup. The good news is that backing up n8n is straightforward if you use PostgreSQL.
What you need to back up
- The PostgreSQL database. Contains the workflows, executions, and encrypted credentials.
- The encryption key file. Without this key, the stored credentials are unrecoverable.
- The n8n volume (
.n8n). Contains configuration and files uploaded by workflows.
Automated backup script
#!/bin/bash
# backup_n8n.sh - Run with cron daily
BACKUP_DIR="/backups/n8n"
DATE=$(date +%Y-%m-%d_%H%M)
RETENTION_DAYS=30
mkdir -p "$BACKUP_DIR"
# 1. PostgreSQL backup
docker exec n8n-postgres pg_dump \
-U n8n_user \
-d n8n \
--format=custom \
> "$BACKUP_DIR/n8n_db_${DATE}.dump"
echo "[BACKUP] Database exported: n8n_db_${DATE}.dump"
# 2. n8n volume backup
docker run --rm \
-v n8n_data:/source:ro \
-v "$BACKUP_DIR":/backup \
alpine tar czf "/backup/n8n_data_${DATE}.tar.gz" -C /source .
echo "[BACKUP] n8n volume exported: n8n_data_${DATE}.tar.gz"
# 3. Copy encryption key (if stored in .env)
cp /path/to/your/.env "$BACKUP_DIR/env_${DATE}.bak"
# 4. Clean old backups
find "$BACKUP_DIR" -type f -mtime +$RETENTION_DAYS -delete
echo "[BACKUP] Completed. Old backups (>$RETENTION_DAYS days) deleted."Set it up with cron:
# Run daily backup at 3:00 AM
0 3 * * * /path/to/backup_n8n.sh >> /var/log/n8n_backup.log 2>&1The n8n encryption key is the most important thing you must back up. If you lose it, all credentials stored in n8n become unrecoverable. Store it in a safe place outside the server.
Credential management: how n8n stores them and what risks exist
n8n encrypts credentials before storing them in the database. It uses AES-256-CBC with the encryption key you define in the configuration. This means that even if someone accesses your database, the credentials are not directly readable.
But there are important nuances:
The encryption key is a single point of failure
If you define the encryption key as an environment variable and the server is lost without a backup of that variable, the encrypted credentials in the database are useless. I’ve seen teams that had a DB backup but not the encryption key. Result: workflows recovered, but all credentials broken.
Credentials in shared workflows
When you export a workflow from n8n, the credentials are NOT exported. This is good for security, but it means that when importing a workflow into another instance you have to reconfigure each credential manually. If you have 15 workflows with 40 credentials, migration is not trivial.
Credential rotation
n8n doesn’t have a built-in credential rotation system. If an API token expires, you have to go to the editor and change it manually. For small teams this is manageable. For setups with dozens of integrations, it starts becoming an operational problem.
Best practices
- Document which credentials each workflow uses.
- Store the encryption key in a secrets manager (Vault, AWS Secrets Manager, or at least an encrypted file outside the server).
- Periodically review which credentials are active and which are obsolete.
- Never store tokens or passwords directly in n8n nodes if you can use environment variables or an external secrets service.
Updates: the maintenance nobody wants to do
n8n publishes releases frequently. Some include security fixes. If your self-hosted instance goes without updating, you accumulate known vulnerabilities.
Update process with Docker
#!/bin/bash
# update_n8n.sh
echo "[UPDATE] Backing up before updating..."
/path/to/backup_n8n.sh
echo "[UPDATE] Stopping n8n..."
docker compose down
echo "[UPDATE] Downloading new version..."
docker compose pull
echo "[UPDATE] Starting n8n..."
docker compose up -d
echo "[UPDATE] Checking status..."
sleep 10
docker compose ps
echo "[UPDATE] Completed."My recommendation: don’t pin the image to latest if you don’t want surprises. Use a specific tag and update in a controlled manner:
image: n8nio/n8n:1.42.1 # instead of n8nio/n8n:latestThis way you can test the new version in a development environment before updating production.
Basic monitoring
A self-hosted n8n without monitoring is a black hole. You don’t know if it’s running, you don’t know if workflows are failing, you don’t know if the disk is filling up.
The bare minimum I always set up:
#!/bin/bash
# health_check_n8n.sh
# Verify that n8n responds
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5678/healthz)
if [ "$HTTP_CODE" != "200" ]; then
echo "[ALERT] n8n is not responding. HTTP code: $HTTP_CODE"
# Here you could send an alert to Slack, email, etc.
curl -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d '{"text": "n8n self-hosted is not responding. Check urgently."}'
fi
# Check disk space
DISK_USAGE=$(df -h / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 85 ]; then
echo "[ALERT] Disk at ${DISK_USAGE}%"
fiMy criteria summarized
| Factor | Self-hosted | Cloud |
|---|---|---|
| Full data control | Yes | No |
| No per-execution cost | Yes | No |
| Internal automations | Good option | Can be overkill |
| High availability needed | Only if you have the infra | Yes |
| Team without ops experience | High risk | Better option |
| Sensitive data with proper infra | Yes | Depends on the provider |
| Very limited budget | Yes | No |
| Few workflows, few executions | Overkill | Better option |
What I wish I had done from day one
If I were to set up a self-hosted n8n from scratch again, I would do three things before creating the first workflow:
- Configure PostgreSQL instead of SQLite. The migration later is possible but annoying.
- Configure automatic backups. Not “I’ll do it tomorrow.” Before the first workflow.
- Store the encryption key in a safe place. Not on the same server. Not on a sticky note. In a secrets manager or in an encrypted file somewhere else.
Self-hosted n8n is a good option when you know what you’re doing and accept the responsibility that comes with control. If what you want is to set up workflows quickly without thinking about servers, backups, or security, the cloud version exists precisely for that. There’s no shame in using it.


