n8n Self Hosted: The Complete Docker Setup and Hardening Guide
Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.
If you’re tired of paying monthly fees for Zapier or Make, running n8n self hosted on your own VPS is one of the highest-leverage moves you can make as a developer or sysadmin. You get unlimited workflow executions, full data ownership, and no per-task billing — for the cost of a $6/month server.
This guide covers everything you need to run n8n self hosted in production: Docker Compose setup, reverse proxy configuration, database choice, backups, and scaling with queue mode.
Why Self-Host n8n
n8n is an open-source workflow automation tool — think Zapier, but with a visual node editor and the ability to write custom JavaScript inside any node. The hosted cloud version is convenient, but it caps executions and charges per active workflow. Self-hosting removes both limits.
When you run n8n self hosted, you also get:
The tradeoff is that you own the ops burden: updates, backups, uptime, and security. This guide addresses all four.
n8n vs Zapier and Make
If you’re evaluating whether to self-host at all, the deciding factor is usually volume and complexity. Zapier and Make are fine for a handful of simple, low-frequency automations. Once you’re running dozens of workflows with branching logic, webhooks, and custom code, the per-task pricing on those platforms gets expensive fast, and n8n self hosted becomes the more economical option, especially at scale.
Prerequisites
Before you start, you’ll need:
If you don’t already have a VPS, DigitalOcean and Hetzner both offer affordable droplets/instances that are more than capable of running n8n comfortably — a $12/month droplet handles most single-user workloads without issue.
Installing n8n with Docker
The fastest, most maintainable way to run n8n self hosted is with Docker Compose. This keeps n8n, its database, and any supporting services isolated and easy to upgrade.
First, create a project directory and the required subfolders:
mkdir -p ~/n8n-stack/local-files
cd ~/n8n-stack
Docker Compose Setup
Create a docker-compose.yml file with the following contents:
version: '3.8'
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=n8n
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U n8n']
interval: 5s
timeout: 5s
retries: 10
n8n:
image: docker.n8n.io/n8nio/n8n:latest
restart: unless-stopped
ports:
- '127.0.0.1:5678:5678'
environment:
- N8N_HOST=${DOMAIN_NAME}
- N8N_PROTOCOL=https
- N8N_PORT=5678
- WEBHOOK_URL=https://${DOMAIN_NAME}/
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- GENERIC_TIMEZONE=UTC
volumes:
- n8n_data:/home/node/.n8n
- ./local-files:/files
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
n8n_data:
Next, create a .env file to hold your secrets — never commit this to version control:
cat > .env << 'EOF'
DOMAIN_NAME=n8n.yourdomain.com
POSTGRES_PASSWORD=change_this_to_a_long_random_string
N8N_ENCRYPTION_KEY=change_this_to_another_long_random_string
EOF
Generate strong random values instead of the placeholders:
openssl rand -hex 32
Run that command twice and paste the outputs into POSTGRES_PASSWORD and N8N_ENCRYPTION_KEY respectively. The encryption key is critical — it encrypts stored credentials, and if you lose it, you lose access to every saved credential in your workflows. Back it up somewhere safe outside the server.
Now bring the stack up:
docker compose up -d
Check that both containers are healthy:
docker compose ps
docker compose logs -f n8n
At this point n8n is running on 127.0.0.1:5678, bound only to localhost. It isn’t reachable from the internet yet — that’s intentional. We’ll expose it safely through a reverse proxy in the next section.
Setting Up a Reverse Proxy with HTTPS
Running n8n behind a reverse proxy with TLS is non-negotiable for anything beyond local testing, since n8n handles webhooks and stored credentials. Nginx with Let’s Encrypt is the simplest combination.
Install Nginx and Certbot on the host:
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
Create a server block at /etc/nginx/sites-available/n8n:
server {
listen 80;
server_name n8n.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass $http_upgrade;
}
}
Enable the site and issue a certificate:
sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d n8n.yourdomain.com
Certbot will automatically rewrite the config for HTTPS and set up renewal. If you’d rather use Traefik with automatic Let’s Encrypt via Docker labels, our Traefik reverse proxy guide walks through that setup in detail, and it pairs well with the Compose file above.
Database Configuration: SQLite vs PostgreSQL
By default, n8n ships with SQLite, which is fine for quick testing but not recommended for production. SQLite struggles under concurrent writes, and a corrupted database file can silently break your entire instance with no easy recovery path. PostgreSQL, as configured in the Compose file above, handles concurrent workflow executions far more reliably and makes backups straightforward with standard tooling like pg_dump.
If you’re already running PostgreSQL for other services, you can point n8n at that instance instead of running a dedicated container — just make sure the DB_POSTGRESDB_HOST environment variable resolves correctly from inside the n8n container’s network.
Securing Your n8n Instance
A self-hosted automation tool that stores API keys and OAuth tokens for every service you connect is a high-value target if compromised. Treat it accordingly.
docker compose pull && docker compose up -d pulls the latest patched releaseN8N_BLOCK_ENV_ACCESS_IN_NODE=true to prevent workflow code nodes from reading host environment variablesFor a full server hardening checklist beyond n8n specifically, see our guide on securing a self-hosted VPS, which covers fail2ban, unattended upgrades, and SSH hardening in more depth.
If uptime matters for your automations — for example, workflows that process incoming webhooks from payment providers — it’s worth pairing your instance with an external monitoring service. BetterStack offers uptime monitoring and status pages that will alert you immediately if your n8n instance or its webhook endpoint goes down, which is far better than discovering a failed workflow days later.
Backups
Backups need to cover two things: the PostgreSQL database (your workflow definitions and execution history) and the n8n_data volume (which holds the encryption key and local file storage).
A simple daily backup script:
#!/bin/bash
set -euo pipefail
BACKUP_DIR=/root/n8n-backups
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"
docker compose exec -T postgres pg_dump -U n8n n8n | gzip > "$BACKUP_DIR/n8n-db-$TIMESTAMP.sql.gz"
docker run --rm -v n8n-stack_n8n_data:/data -v "$BACKUP_DIR":/backup alpine
tar czf "/backup/n8n-data-$TIMESTAMP.tar.gz" -C /data .
find "$BACKUP_DIR" -type f -mtime +14 -delete
Schedule it with cron:
crontab -e
# Add the following line:
0 3 * * * /root/n8n-backup.sh >> /var/log/n8n-backup.log 2>&1
Copy the backups off the server itself — to object storage or another machine — since a backup that lives only on the server it’s protecting isn’t a real backup.
Scaling n8n with Queue Mode
If you’re running high-volume or long-running workflows, the default setup can become a bottleneck because a single n8n process handles both the UI and execution. Queue mode splits this: a main process handles the editor and webhook triggers, while separate worker processes pull jobs from a Redis-backed queue and execute them.
To enable it, add a Redis service to your Compose file and set these additional environment variables on the n8n service:
redis:
image: redis:7-alpine
restart: unless-stopped
EXECUTIONS_MODE=queue
QUEUE_BULL_REDIS_HOST=redis
Then run a separate worker container using the same image with the command n8n worker. This lets you scale workers horizontally as workflow volume grows, without touching the main instance.
Monitoring and Observability
Once n8n is handling anything business-critical, blind spots become expensive. At minimum, track:
docker compose ps and restart policiesEXECUTIONS_DATA_PRUNEOur Docker Compose monitoring guide shows how to wire up Prometheus and Grafana against a Compose stack like this one if you want dashboards rather than just log tailing.
Recommended: Want to explore DigitalOcean yourself? DigitalOcean is a direct vendor link (not an affiliate/tracked link).
FAQ
Is n8n free to self-host?
Yes. The core n8n software is fair-code licensed and free to self-host with no execution limits. You only pay for the server it runs on and optional enterprise features if you need SSO or advanced permissions.
How much does it cost to run n8n self hosted?
A small VPS with 1-2 vCPUs and 2GB of RAM, typically $6-12/month from providers like DigitalOcean or Hetzner, is enough for most individual or small-team workloads.
Do I need PostgreSQL, or can I stick with SQLite?
SQLite works for testing, but PostgreSQL is strongly recommended for production because it handles concurrent executions more reliably and supports standard backup tooling.
Can I run n8n without Docker?
Yes, n8n can be installed via npm directly on the host, but Docker is recommended because it isolates dependencies, simplifies upgrades, and matches the environment n8n is tested against.
How do I update a self-hosted n8n instance safely?
Back up your database and volumes first, then run docker compose pull followed by docker compose up -d. Check the n8n release notes for breaking changes before upgrading across major versions.
Is it safe to expose n8n’s webhook URLs publicly?
Yes, that’s expected usage, but only expose them behind HTTPS via a reverse proxy, and enable user authentication on the editor UI so the workflow builder itself isn’t publicly accessible.
Final Thoughts
Running n8n self hosted takes maybe 30 minutes of setup time and gives you a private, unlimited automation platform that competes with tools costing hundreds of dollars a month at scale. Start with the Docker Compose setup above, put it behind HTTPS, automate your backups, and you’ll have a production-grade instance before the end of the afternoon.
Leave a Reply