How to Deploy n8n on a Dedicated Server
A production-ready guide to self-hosting n8n workflow automation on bare metal with Docker, PostgreSQL, Nginx, SSL, and automated backups.
Why Self-Host n8n on Dedicated Hardware?
n8n is an open-source workflow automation platform that lets you connect APIs, services, and databases into automated workflows — similar to Zapier or Make, but with full control over your data and no per-execution pricing. While n8n offers a cloud-hosted option, self-hosting on a dedicated server provides several advantages:
- No execution limits. n8n Cloud plans cap the number of workflow executions per month. Self-hosted n8n has no limits — run as many workflows as your hardware can handle.
- Data privacy. All workflow data, credentials, and execution logs stay on your server. Essential for handling sensitive customer data, API keys, or internal business processes.
- Performance. Dedicated hardware means your workflows execute faster with no shared-resource contention. Complex workflows with many nodes or large data payloads benefit significantly.
- Cost efficiency. n8n Cloud's Pro plan starts at $20/month with execution limits. A dedicated server running n8n alongside other services costs $49/month with unlimited capacity.
This tutorial walks through a complete production deployment on a Hetzner dedicated server. The same steps work on any dedicated server from any provider.
Prerequisites
- A dedicated server with at least 4 CPU cores, 16GB RAM, and 100GB+ SSD/NVMe storage (we recommend the Hetzner AX42 at $49/mo)
- A registered domain name (e.g.,
n8n.yourdomain.com) with DNS access - SSH access to a local terminal (macOS Terminal, Linux shell, or PuTTY on Windows)
- Basic familiarity with Linux command line
Step 1: Order a Hetzner Dedicated Server
Head to Hetzner's dedicated server page and order an AX42 or higher. For n8n specifically, the AX42 is more than sufficient — its 8-core AMD Ryzen 7 PRO 8700GE, 64GB RAM, and NVMe storage will handle hundreds of concurrent workflows without breaking a sweat.
During the order process:
- Select your preferred datacenter location (choose the one closest to your users or the APIs you integrate with most frequently)
- Choose Ubuntu 24.04 LTS as the operating system — it is the most well-supported for Docker deployments
- Select either software RAID 1 for redundancy or no RAID if you prefer to manage storage yourself
- Add your SSH public key during setup to enable key-based authentication from the start
Hetzner typically delivers dedicated servers within a few hours. You will receive an email with the server's IP address and initial login credentials.
Step 2: SSH Setup and Initial Server Configuration
Once your server is ready, connect via SSH and perform initial hardening.
# Connect to your server (replace with your IP)
ssh root@YOUR_SERVER_IP
# Update the system
apt update && apt upgrade -y
# Create a non-root user
adduser deploy
usermod -aG sudo deploy
# Copy SSH keys to the new user
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
# Harden SSH: disable password auth and root login
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart sshd
# Set up UFW firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw enable
Now log out and reconnect as the deploy user to confirm key-based authentication works. From this point forward, all commands should be run as the deploy user with sudo when needed.
# Reconnect as deploy user
ssh deploy@YOUR_SERVER_IP
# Set the timezone
sudo timedatectl set-timezone UTC
# Enable automatic security updates
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades Step 3: Install Docker and Docker Compose
Install Docker Engine using the official Docker repository for the latest stable version.
# Install prerequisites
sudo apt install -y ca-certificates curl gnupg
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the Docker repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine and Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# Add your user to the docker group (avoids needing sudo for docker)
sudo usermod -aG docker deploy
# Apply group changes (or log out and back in)
newgrp docker
# Verify installation
docker --version
docker compose version Step 4: Create n8n Docker Compose Configuration
Create a project directory and set up the Docker Compose file. This configuration runs n8n with PostgreSQL as the database backend (recommended for production over the default SQLite) and includes proper volume mounts for data persistence.
# Create project directory
mkdir -p ~/n8n && cd ~/n8n
# Create environment file
cat > .env << 'ENVEOF'
# n8n Configuration
N8N_HOST=n8n.yourdomain.com
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.yourdomain.com/
N8N_ENCRYPTION_KEY=your-random-encryption-key-change-this
GENERIC_TIMEZONE=UTC
# PostgreSQL
POSTGRES_USER=n8n
POSTGRES_PASSWORD=your-strong-db-password-change-this
POSTGRES_DB=n8n
# Optional: Set execution data pruning
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168
ENVEOF Important: Replace n8n.yourdomain.com with your actual domain, and generate strong random values for N8N_ENCRYPTION_KEY and POSTGRES_PASSWORD. You can generate a random key with openssl rand -hex 32.
# Create docker-compose.yml
cat > docker-compose.yml << 'COMPOSEEOF'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=${N8N_HOST}
- N8N_PORT=${N8N_PORT}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- WEBHOOK_URL=${WEBHOOK_URL}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- EXECUTIONS_DATA_PRUNE=${EXECUTIONS_DATA_PRUNE}
- EXECUTIONS_DATA_MAX_AGE=${EXECUTIONS_DATA_MAX_AGE}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-alpine
container_name: n8n-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
n8n_data:
postgres_data:
COMPOSEEOF
# Start the stack
docker compose up -d
# Verify both containers are running
docker compose ps
At this point, n8n should be running on 127.0.0.1:5678. It is bound to localhost only for security — we will expose it through Nginx with SSL in the next steps.
Step 5: Set Up Nginx Reverse Proxy
Install Nginx and configure it as a reverse proxy. This handles SSL termination, WebSocket connections (required for n8n's real-time features), and serves as a security layer in front of the application.
# Install Nginx
sudo apt install -y nginx
# Create the n8n site configuration
sudo tee /etc/nginx/sites-available/n8n << 'NGINXEOF'
server {
listen 80;
server_name n8n.yourdomain.com;
# Redirect HTTP to HTTPS (will work after Certbot runs)
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name n8n.yourdomain.com;
# SSL certificates (Certbot will populate these)
# ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-XSS-Protection "1; mode=block" always;
# Proxy to n8n
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_buffering off;
proxy_cache off;
chunked_transfer_encoding off;
# Timeouts for long-running workflows
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
proxy_send_timeout 300s;
}
# Webhook endpoints — longer timeouts
location /webhook/ {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
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_read_timeout 600s;
}
client_max_body_size 50M;
}
NGINXEOF
# Enable the site and remove the default
sudo ln -sf /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
# Test configuration (will show warnings about SSL certs - that's expected)
sudo nginx -t
# For now, comment out the SSL server block and just use port 80
# We'll enable SSL in the next step with Certbot
sudo systemctl restart nginx
Before proceeding, make sure your domain's DNS A record points to your server's IP address. You can verify with dig n8n.yourdomain.com — it should return your server's IP.
Step 6: SSL with Certbot
Install Certbot to obtain a free SSL certificate from Let's Encrypt. Certbot will automatically configure Nginx for SSL and set up auto-renewal.
# Install Certbot and the Nginx plugin
sudo apt install -y certbot python3-certbot-nginx
# Obtain and install the SSL certificate
sudo certbot --nginx -d n8n.yourdomain.com \
--non-interactive \
--agree-tos \
--email your-email@example.com \
--redirect
# Verify auto-renewal is configured
sudo certbot renew --dry-run
# Restart Nginx with SSL enabled
sudo systemctl restart nginx Certbot automatically modifies your Nginx configuration to include the SSL certificate paths and enables the HTTPS server block. It also sets up a systemd timer that renews the certificate automatically before it expires.
You should now be able to access n8n at https://n8n.yourdomain.com. The first time you visit, n8n will prompt you to create an owner account. Choose a strong password — this account has full administrative access.
Step 7: Backup Strategy
Automated backups are critical. This script backs up both the PostgreSQL database and n8n's configuration data daily, retaining 14 days of backups.
# Create backup directory
sudo mkdir -p /opt/backups/n8n
sudo chown deploy:deploy /opt/backups/n8n
# Create backup script
cat > ~/n8n/backup.sh << 'BACKUPEOF'
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/backups/n8n"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=14
echo "[$(date)] Starting n8n backup..."
# Backup PostgreSQL database
docker exec n8n-postgres pg_dump -U n8n -d n8n \
| gzip > "${BACKUP_DIR}/n8n_db_${TIMESTAMP}.sql.gz"
# Backup n8n data volume
docker run --rm \
-v n8n_n8n_data:/data:ro \
-v ${BACKUP_DIR}:/backup \
alpine tar czf "/backup/n8n_data_${TIMESTAMP}.tar.gz" -C /data .
# Delete backups older than retention period
find "${BACKUP_DIR}" -name "n8n_*.gz" -mtime +${RETENTION_DAYS} -delete
echo "[$(date)] Backup complete: n8n_db_${TIMESTAMP}.sql.gz, n8n_data_${TIMESTAMP}.tar.gz"
echo "[$(date)] Cleaned up backups older than ${RETENTION_DAYS} days"
BACKUPEOF
chmod +x ~/n8n/backup.sh
# Test the backup
~/n8n/backup.sh
# Schedule daily backups at 3 AM UTC
(crontab -l 2>/dev/null; echo "0 3 * * * /home/deploy/n8n/backup.sh >> /opt/backups/n8n/backup.log 2>&1") | crontab - For offsite backup redundancy, consider adding an rsync step that copies the backup files to a remote server or an S3-compatible object storage service. A single server with no offsite backups is a single point of failure.
# Optional: Sync backups to a remote server
# Add this line to the backup script before the cleanup step:
# rsync -avz --delete ${BACKUP_DIR}/ user@remote-server:/backups/n8n/
# Optional: Sync to S3-compatible storage (e.g., Hetzner Object Storage)
# Install s3cmd: sudo apt install -y s3cmd
# Configure: s3cmd --configure
# Add to backup script:
# s3cmd sync ${BACKUP_DIR}/ s3://your-bucket/n8n-backups/ Post-Deployment Tips
- Update n8n: Pull the latest image with
cd ~/n8n && docker compose pull && docker compose up -d. Check the release notes before major version upgrades. - Monitor resources: Install
htopfor quick checks, or set up Netdata (bash <(curl -Ss https://my-netdata.io/kickstart.sh)) for a full monitoring dashboard. - Set up log rotation: Docker logs can grow large. Configure log rotation in
/etc/docker/daemon.json:{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } - Run additional services: Your dedicated server has plenty of capacity. Consider running additional containers like Uptime Kuma (monitoring), Plausible (analytics), or Portainer (Docker management) alongside n8n.
Alternative Server Providers
While this tutorial uses Hetzner, n8n runs equally well on any dedicated server. Here are two alternatives depending on your needs.
Contabo — Even Cheaper
If $49/month is still more than you want to spend, Contabo's Dedicated S at $129/month offers an AMD EPYC series processor with 64GB RAM. While the hardware is older, it is more than sufficient for n8n. Contabo also has datacenter locations in Asia-Pacific if your workflows integrate primarily with APAC services.
Visit ContaboOVHcloud — More Regions
OVHcloud's Rise-1 at $64/month provides an 8-core Intel Xeon with 32GB RAM and includes robust anti-DDoS protection. If your n8n instance receives webhooks from public services, the built-in DDoS mitigation adds a layer of protection that Hetzner and Contabo do not include at this price tier.
Visit OVHcloudNot sure which server to choose? Use our Server Finder to filter by budget and specs, or check our Best Budget Dedicated Servers guide.
Get the Free Server Buyer's Checklist
A step-by-step guide to choosing the right dedicated server for your workload. Plus, get notified when we publish new benchmarks.
No spam. Unsubscribe anytime.