If you're looking for a powerful automation platform that won't break the bank, self-hosting n8n on Hetzner Cloud might be the perfect solution. In this guide, I'll walk you through how we set up a production-ready n8n instance with enterprise-grade security for just ~$6.
Why Self-Host n8n?
n8n is an open-source workflow automation tool that rivals expensive solutions like Zapier or Make. By self-hosting, you get:
- Complete data privacy and control
- No workflow execution limits
- Custom integrations and code
- Full API access
- Cost-effective scaling
The Setup: What We're Building
Here's what we'll deploy:
- Server: Hetzner Cloud CX22 (2 vCPU, 4GB RAM)
- Location: Falkenstein, Germany
- Services: n8n (Docker), Caddy (reverse proxy with automatic SSL)
- Security: UFW firewall, Fail2ban, audit logging, automated updates
- Cost: ~$6 / month
Step 1: Creating the Hetzner Cloud Server
First, create a Hetzner Cloud account and set up a new server:
# Using Hetzner CLI (optional)
hcloud server create \
--name n8n-server \
--type cx22 \
--image ubuntu-22.04 \
--location fsn1 \
--ssh-key ~/.ssh/id_rsa.pub
Pro tip: The CX22 instance type offers the best price-performance ratio for n8n. With 2 vCPUs and 4GB RAM, it can handle substantial workflows without issues.
Step 2: Initial Server Security Hardening
Security first! Here's our hardening checklist:
Create a non-root user
# As root
adduser admin
usermod -aG sudo admin
mkdir -p /home/admin/.ssh
cp ~/.ssh/authorized_keys /home/admin/.ssh/
chown -R admin:admin /home/admin/.ssh
chmod 700 /home/admin/.ssh
chmod 600 /home/admin/.ssh/authorized_keys
Disable root login and password authentication
# Edit SSH config
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd
Set up UFW firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
Install and configure Fail2ban
apt update && apt install -y fail2ban
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 2h
maxretry = 3
findtime = 10m
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
EOF
systemctl restart fail2ban
Enable automatic security updates
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
Set up audit logging
apt install -y auditd
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/shadow -p wa -k shadow_changes
auditctl -w /var/log/auth.log -p wa -k auth_log
service auditd restart
Step 3: Installing Docker and Docker Compose
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# Install Docker Compose
apt install -y docker-compose-plugin
# Verify installation
docker --version
docker compose version
Step 4: Setting Up n8n with Docker Compose
Create the n8n directory and docker-compose file:
mkdir -p /root/n8n
cd /root/n8n
Create a secure docker-compose.yml
:
version: '3.8'
services:
n8n:
image: n8nio/n8n
container_name: n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678" # Only accessible locally
environment:
- N8N_HOST=n8n.yourdomain.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.yourdomain.com/
- GENERIC_TIMEZONE=Europe/Berlin
volumes:
- n8n_data:/home/node/.n8n
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
- /home/node/.n8n/cache
volumes:
n8n_data:
Security insights:
- Binding to
127.0.0.1
ensures n8n is only accessible through our reverse proxy -
read_only: true
prevents container filesystem modifications -
no-new-privileges
prevents privilege escalation - Using tmpfs for temporary files improves security and performance
Step 5: Setting Up Caddy as Reverse Proxy
Caddy provides automatic HTTPS certificates and is much simpler than nginx:
# Install Caddy
apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install -y caddy
Configure Caddy:
cat > /etc/caddy/Caddyfile << 'EOF'
n8n.yourdomain.com {
reverse_proxy localhost:5678 {
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Proto {scheme}
}
# Security headers
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
X-XSS-Protection "1; mode=block"
Referrer-Policy strict-origin-when-cross-origin
-Server
}
# Enable compression
encode gzip
}
EOF
systemctl restart caddy
Step 6: Starting n8n
cd /root/n8n
docker compose up -d
Visit https://n8n.yourdomain.com
and complete the initial setup!
Advanced Security Measures
1. Implement IP-based access control
If you have a static IP, limit access to n8n:
n8n.yourdomain.com {
@allowed {
remote_ip 1.2.3.4 192.168.0.0/16
}
handle @allowed {
reverse_proxy localhost:5678
}
handle {
respond "Access Denied" 403
}
}
2. Set up Fail2ban for n8n
Create a custom filter for failed n8n login attempts:
cat > /etc/fail2ban/filter.d/n8n-auth.conf << 'EOF'
[Definition]
failregex = ^.* 401 .* "POST /rest/auth/login.*" .*$
ignoreregex =
EOF
# Add to jail.local
cat >> /etc/fail2ban/jail.local << 'EOF'
[n8n-auth]
enabled = true
port = http,https
filter = n8n-auth
logpath = /var/log/caddy/access.log
maxretry = 5
bantime = 1h
EOF
3. Regular backups
Create a backup script:
#!/bin/bash
# /root/backup-n8n.sh
BACKUP_DIR="/root/backups"
mkdir -p $BACKUP_DIR
docker compose -f /root/n8n/docker-compose.yml down
docker run --rm -v n8n_data:/data -v $BACKUP_DIR:/backup alpine tar czf /backup/n8n-backup-$(date +%Y%m%d).tar.gz -C /data .
docker compose -f /root/n8n/docker-compose.yml up -d
# Keep only last 7 backups
find $BACKUP_DIR -name "n8n-backup-*.tar.gz" -mtime +7 -delete
Add to crontab:
0 3 * * * /root/backup-n8n.sh
Performance Optimization Tips
- Enable Redis for scaling: Add a Redis container for better performance with multiple workers
- Use external database: For production workloads, consider PostgreSQL instead of SQLite
-
Monitor resources: Install
htop
and set up monitoring with Prometheus/Grafana - Optimize workflows: Use webhook triggers instead of polling when possible
Cost Breakdown
- Hetzner CX22 Server: ~$6/month
- Domain (use a subdomain): ~$0/year
- Total monthly cost: ~$6/month
Compare this to n8n Cloud's pricing starting at $20/month for just 2,500 workflow executions!
Lessons Learned
- Start with security: It's easier to harden from the beginning than retrofit later
- Docker adds complexity but worth it: Container isolation and easy updates outweigh the overhead
- Caddy > Nginx for simple setups: Automatic HTTPS saves hours of configuration
- Monitor everything: Set up alerts for disk space, CPU usage, and failed login attempts
- Document everything: Future you will thank present you
Troubleshooting Common Issues
n8n won't start
# Check logs
docker logs n8n
# Common fix: permissions on data volume
docker volume rm n8n_data # Warning: deletes data!
SSL certificate issues
# Caddy logs
journalctl -u caddy -f
# Force certificate renewal
caddy reload --config /etc/caddy/Caddyfile
High memory usage
# Add memory limits to docker-compose.yml
deploy:
resources:
limits:
memory: 3g
Conclusion
For less than the price of a Netflix subscription, you can run your own powerful automation platform with complete control over your data. The setup takes about an hour, but the flexibility and cost savings make it worthwhile for anyone serious about automation.
Have you set up your own n8n instance? What challenges did you face? Let me know in the comments!
Resources:
Top comments (0)