Intro
n8n is one of the most powerful source-available workflow automation tools available today. Unlike SaaS alternatives such as Zapier or Make, n8n gives you full control over your data and automations — but only when you host it yourself. A Linux VPS is the ideal environment for this: always-on, affordable, and fully under your control.
This guide walks you through the complete process of setting up a production-ready, self-hosted n8n instance on a Linux VPS. You will go from a blank server to a fully secured, SSL-enabled n8n deployment with Docker, Nginx, PostgreSQL, automated backups, and a hardened security configuration.
Key Takeaways
- n8n on a VPS gives you unlimited workflows, no usage caps, and full data ownership
- Docker is the recommended installation method for easier updates and isolation
- PostgreSQL is the recommended database for production workloads (not SQLite)
- A reverse proxy (Nginx) with Let's Encrypt SSL is required for production use
- Your VPS should have at least 1 vCPU and 2 GB RAM for a smooth n8n experience with PostgreSQL
- Regular automated backups protect your workflow data and credentials
What is n8n?
n8n (pronounced "n-eight-n") is a source-available workflow automation platform that lets you connect apps, APIs, and services using a visual node-based editor. It supports over 400 integrations out of the box and allows you to run custom JavaScript or Python code inside workflows.
The key difference between n8n and its competitors comes down to three things: ownership, cost, and flexibility.
**Ownership: **When you self-host n8n, your workflow data, credentials, and execution logs never leave your server. For teams handling sensitive customer data, financial records, or proprietary business logic, this matters enormously. Cloud platforms store your automation logic on their infrastructure, subject to their data retention policies and security posture.
**Cost: **The n8n cloud plan charges per workflow execution, which adds up fast for high-volume automations. A mid-sized team running thousands of executions per day can spend hundreds of dollars monthly. On your own VPS, once you pay for the server, executions are unlimited at no extra cost.
**Flexibility: **Self-hosted n8n lets you install community nodes, run arbitrary code inside workflows, connect to internal services not accessible from the public internet, and configure every aspect of the deployment to your liking. Cloud plans restrict many of these capabilities.
Prerequisites
Before you begin, make sure you have:
- A Linux VPS running Ubuntu 22.04 or 24.04 (recommended). Check that your provider offers Ubuntu on the plan size you pick, as some entry-level plans only offer other distributions
- A non-root user with sudo privileges
- A domain name pointed to your VPS IP address (e.g., n8n.yourdomain.com)
- SSH access to your server
- Basic familiarity with the Linux command line
Choosing the Right VPS
The server you choose has a direct impact on n8n's performance. At a minimum you need 1 vCPU, 2 GB RAM, and 20 GB SSD storage. The extra RAM compared to a minimal SQLite-based install is needed because PostgreSQL, Nginx, and n8n all run at the same time.
The All-in-One Platform for Effective SEO
Behind every successful business is a strong SEO campaign. But with countless optimization tools and techniques out there to choose from, it can be hard to know where to start. Well, fear no more, cause I've got just the thing to help. Presenting the Ranktracker all-in-one platform for effective SEO
We have finally opened registration to Ranktracker absolutely free!
Create a free accountOr Sign in using your credentials
For teams with multiple users, complex multi-step workflows, or high webhook throughput, upgrade to 2 vCPU and 4 GB RAM. Storage should be at minimum 40 GB if you plan to keep long execution logs.
PetroSky offers NVMe VPS plans in Paris and Montreal, with n8n available as a one-click application at checkout: an open-source install script deploys n8n with Docker Compose and sets up HTTPS automatically through Caddy and Let’s Encrypt. This guide covers the manual route, which works on any provider.
When selecting a VPS provider, also consider geographic location. Host your n8n instance in the region closest to the services it will connect to most often — this reduces API latency in your workflow executions.
Step 1: Update Your Server
SSH into your VPS and run a full system update before installing anything:
sudo apt update && sudo apt upgrade -y
This ensures you have the latest security patches and that all system libraries are current before adding Docker and other software on top.
Reboot if a kernel update was applied:
sudo reboot
After reconnecting via SSH, create a dedicated system user for running n8n. Running services as root is a security risk:
sudo adduser n8nuser
sudo usermod -aG sudo n8nuser
su - n8nuser
Step 2: Install Docker and Docker Compose
n8n's official recommended installation method is Docker. Docker isolates n8n and its dependencies from the rest of your system, making updates and rollbacks straightforward.
Install Docker using the official convenience script:
curl -fsSL https://get.docker.com | sudo sh
Docker’s documentation notes that the convenience script isn’t recommended for production environments, because it installs the latest release without letting you choose a version. If you want to pin Docker versions, use the apt repository method from Docker’s Ubuntu install guide (docs.docker.com/engine/install/ubuntu/) instead.
Add your user to the Docker group so you can run Docker commands without sudo:
sudo usermod -aG docker $USER
newgrp docker
Verify Docker is running correctly:
docker --version
docker compose version
Both commands should return version strings without errors. If Docker Compose is not found, install it manually:
sudo apt install docker-compose-plugin -y
Step 3: Set Up PostgreSQL for Production
By default, n8n uses SQLite as its database. SQLite is fine for personal use and light workflows, but it handles concurrent writes poorly, which shows up as slowdowns and “database is locked” errors once many workflows run at the same time. For a production deployment, PostgreSQL is the right choice.
The All-in-One Platform for Effective SEO
Behind every successful business is a strong SEO campaign. But with countless optimization tools and techniques out there to choose from, it can be hard to know where to start. Well, fear no more, cause I've got just the thing to help. Presenting the Ranktracker all-in-one platform for effective SEO
We have finally opened registration to Ranktracker absolutely free!
Create a free accountOr Sign in using your credentials
Run PostgreSQL inside Docker alongside n8n:
mkdir ~/n8n && cd ~/n8n
Create an environment file to store your secrets securely. Never hardcode passwords in your compose file:
nano ~/n8n/.env
Add the following, replacing the placeholder values with strong unique passwords:
POSTGRES_USER=n8n
POSTGRES_PASSWORD=your_strong_db_password_here
POSTGRES_DB=n8n
N8N_ENCRYPTION_KEY=your_random_32_char_string_here
Generate a secure encryption key with:
openssl rand -hex 16
Use the output as your N8N_ENCRYPTION_KEY. This key encrypts credentials stored in n8n — if you lose it, all saved credentials become unrecoverable.
Step 4: Create the n8n Docker Compose File
Create the docker-compose.yml file in your n8n directory:
nano ~/n8n/docker-compose.yml
Paste the following configuration:
services:
postgres:
image: postgres:16-alpine
restart: always
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 -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=n8n.yourdomain.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://n8n.yourdomain.com/
- GENERIC_TIMEZONE=Europe/London
- 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}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- EXECUTIONS_MODE=regular
- N8N_LOG_LEVEL=info
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
n8n_data:
Configuration notes:
- The port binding 127.0.0.1:5678:5678 ensures n8n is only accessible locally. Nginx will handle all external traffic — never expose port 5678 directly to the internet.
- depends_on with the service_healthy condition ensures n8n waits for PostgreSQL to be fully ready before starting. Without this, n8n will fail on first boot if the database is not yet accepting connections.
- N8N_LOG_LEVEL=info gives you useful operational logs without flooding disk storage the way debug would.
Step 5: Install and Configure Nginx
Nginx will act as a reverse proxy, forwarding HTTPS traffic from port 443 to your n8n container on port 5678. It also handles SSL termination, which means n8n itself does not need to manage certificates.
sudo apt install nginx -y
Remove the default site and create a new server block for n8n:
sudo rm /etc/nginx/sites-enabled/default
sudo nano /etc/nginx/sites-available/n8n
Paste the following:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
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 $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;
chunked_transfer_encoding on;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 90s;
client_max_body_size 50m;
}
}
The map block at the top correctly handles WebSocket upgrade headers. n8n uses WebSockets for real-time updates in the editor, so this configuration is important — without it, the visual editor will not function correctly.
The client_max_body_size 50m directive allows workflows to receive large webhook payloads. The default Nginx limit is 1 MB, which is too small for workflows processing file uploads or large JSON responses.
Enable the configuration and test it:
sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 6: Issue an SSL Certificate with Let's Encrypt
Install Certbot and the Nginx plugin:
sudo apt install certbot python3-certbot-nginx -y
Request a certificate for your domain:
sudo certbot --nginx -d n8n.yourdomain.com
Follow the prompts. When asked whether to redirect HTTP traffic to HTTPS, choose Yes (option 2). Certbot will automatically update your Nginx configuration to handle this redirect and install the full certificate chain.
Verify auto-renewal is configured correctly:
sudo certbot renew --dry-run
Certbot installs a systemd timer that renews certificates automatically every 12 hours. Certificates are renewed when they are within 30 days of expiry. You should never need to manually renew.
Step 7: Start n8n
Return to your n8n directory and start all services:
cd ~/n8n
docker compose up -d
Docker will pull the PostgreSQL and n8n images, create the volumes, start PostgreSQL first, wait for the health check to pass, then start n8n. This process takes 1–3 minutes on first run.
Check that both containers are running:
docker compose ps
You should see both postgres and n8n listed with status Up. If n8n shows Restarting, check the logs:
docker compose logs n8n --tail=50
Once both containers are healthy, open your browser and navigate to https://n8n.yourdomain.com. You will be prompted to create an owner account with an email address and password. This account has full admin access to your n8n instance.
Step 8: Configure n8n to Start on Boot
The restart: always directive in your docker-compose.yml ensures all containers restart automatically on crash or server reboot.
Verify this by rebooting your server:
sudo reboot
After reconnecting via SSH, check that n8n came back up automatically:
docker compose -f ~/n8n/docker-compose.yml ps
Both containers should show as running without any manual intervention.
Step 9: Set Up Automated Backups
Your n8n data — workflows, credentials, and execution history — lives in two places: the PostgreSQL database and the n8n_data Docker volume (which stores encryption keys and some configuration). Back up both regularly.
Create a backup script:
nano ~/n8n/backup.sh
Paste the following:
#!/bin/bash
BACKUP_DIR="/home/n8nuser/backups"
DATE=$(date +%Y-%m-%d_%H-%M)
LOG_FILE="$BACKUP_DIR/backup.log"
# Ensure backup directory exists before anything else
mkdir -p "$BACKUP_DIR"
# Dump PostgreSQL
DUMP_FILE="$BACKUP_DIR/n8n_db_$DATE.sql"
docker compose -f /home/n8nuser/n8n/docker-compose.yml exec -T postgres \
pg_dump -U n8n n8n > "$DUMP_FILE"
# Verify the dump succeeded and is non-empty
if [ $? -ne 0 ] || [ ! -s "$DUMP_FILE" ]; then
echo "ERROR: Database backup failed at $DATE" >> "$LOG_FILE"
rm -f "$DUMP_FILE"
exit 1
fi
# Back up the encryption key and environment variables
cp /home/n8nuser/n8n/.env "$BACKUP_DIR/n8n_env_$DATE.bak"
# Back up the n8n_data volume (instance config and installed community nodes)
docker run --rm -v n8n_n8n_data:/data -v "$BACKUP_DIR":/backup alpine \
tar czf /backup/n8n_data_$DATE.tar.gz -C /data .
# Compress the database dump
gzip "$DUMP_FILE"
# Keep only the last 7 days of backups
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete
find "$BACKUP_DIR" -name "*.bak" -mtime +7 -delete
find "$BACKUP_DIR" -name "n8n_data_*.tar.gz" -mtime +7 -delete
echo "Backup completed: n8n_db_$DATE.sql.gz" >> "$LOG_FILE"
The volume name n8n_n8n_data comes from Docker Compose prefixing the volume with the project folder name (n8n). Confirm yours with docker volume ls.
Make the script executable and schedule it with cron:
chmod +x ~/n8n/backup.sh
crontab -e
Add this line to run the backup every day at 2 AM:
0 2 * * * /home/n8nuser/n8n/backup.sh >> /home/n8nuser/backups/backup.log 2>&1
For offsite redundancy, consider syncing your backup directory to an S3-compatible object storage provider using rclone or aws s3 sync.
Step 10: Enable Basic Security Hardening
Set Up a Firewall
Install UFW and allow only the ports your server needs:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Verify the rules are active:
sudo ufw status verbose
You should see port 22 (SSH), 80 (HTTP), and 443 (HTTPS) allowed. One caveat: Docker writes its own firewall rules and bypasses UFW for published container ports. Port 5678 stays private because the compose file binds it to 127.0.0.1, not because of UFW, so never change that binding to 0.0.0.0.
Disable Password Authentication for SSH
If you are using SSH keys (which you should be), disable password-based SSH login entirely. First, make sure the n8nuser account can log in with your key. From your local machine, run:
ssh-copy-id n8nuser@your_server_ip
Then edit the SSH server configuration:
sudo nano /etc/ssh/sshd_config
Set the following values:
PasswordAuthentication no
PermitRootLogin no
sudo systemctl restart ssh
On many Ubuntu cloud images, a file in /etc/ssh/sshd_config.d/ (often 50-cloud-init.conf) sets PasswordAuthentication yes, and it wins because sshd uses the first value it reads. Check the setting that is actually in effect:
sudo sshd -T | grep -i passwordauthentication
It should print passwordauthentication no. If it doesn’t, change the value in the override file and restart SSH again.
Do not close your current SSH session until you have verified that key-based login works in a new terminal window.
Rate-Limit the n8n Login Page
Add rate limiting to slow down brute-force attacks against the n8n login endpoint.
The All-in-One Platform for Effective SEO
Behind every successful business is a strong SEO campaign. But with countless optimization tools and techniques out there to choose from, it can be hard to know where to start. Well, fear no more, cause I've got just the thing to help. Presenting the Ranktracker all-in-one platform for effective SEO
We have finally opened registration to Ranktracker absolutely free!
Create a free accountOr Sign in using your credentials
**Important: **The limit_req_zone directive must go inside the http {} block in your main Nginx config — not inside the server block. Open the main config:
sudo nano /etc/nginx/nginx.conf
Add this line inside the existing http {} block:
http {
...
limit_req_zone $binary_remote_addr zone=n8n_login:10m rate=5r/m;
...
}
Then in your n8n site config (/etc/nginx/sites-available/n8n), add this location block inside the server block that listens on port 443 (Certbot created it in Step 6), next to the existing location /. Nginx does not carry proxy headers over from location /, so they are repeated here:
location /rest/login {
limit_req zone=n8n_login burst=5 nodelay;
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;
}
Reload Nginx to apply:
sudo nginx -t && sudo systemctl reload nginx
This limits each IP address to roughly 5 login attempts per minute, with a small burst allowance.
Keep n8n and PostgreSQL Updated
Pull and restart with the latest images regularly:
cd ~/n8n
docker compose pull
docker compose up -d
Docker will pull the latest n8nio/n8n and postgres images and restart only the containers that have a newer version available.
For production, consider pinning the n8n image to a specific version tag (docker.n8n.io/n8nio/n8n:<version>, taken from n8n’s release notes) and upgrading deliberately, so a routine pull never jumps a major version before you have read the changelog.
Troubleshooting Common Issues
n8n Container Keeps Restarting
Check the container logs immediately after the restart loop begins:
docker compose logs n8n --tail=100
Common causes:
- Database connection failure: Ensure PostgreSQL is healthy with docker compose ps. Check that the POSTGRES_PASSWORD in your .env file matches what the database was initialized with.
- Encryption key missing or changed: If N8N_ENCRYPTION_KEY is not set or was changed after the first run, n8n cannot decrypt stored credentials and will crash.
- Insufficient RAM: PostgreSQL and n8n together can use 1–1.5 GB of RAM. Check available memory with free -h.
Webhooks Not Working
Webhooks require n8n to be reachable from the internet on your domain. Verify:
- Your domain DNS A record points to your VPS IP: dig n8n.yourdomain.com
- Nginx is running: sudo systemctl status nginx
- Port 443 is open: sudo ufw status
- The WEBHOOK_URL environment variable matches your public domain exactly, including the trailing slash
The n8n Editor is Not Loading Properly
If the visual editor loads but displays blank workflow canvas or real-time updates stop working, the WebSocket connection is failing. Check the map $http_upgrade block is present in your Nginx config and that you are using the $connection_upgrade variable — not the literal string "upgrade" — in the proxy_set_header Connection line.
SSL Certificate Errors
If Certbot fails with a domain validation error, check that port 80 is accessible from the internet:
curl -I http://n8n.yourdomain.com
If the request times out, your VPS provider's firewall may be blocking port 80. Check the cloud console firewall rules in addition to UFW.
Conclusion
Self-hosting n8n on a Linux VPS gives you a production-ready workflow automation platform with no execution limits, full data privacy, and the flexibility to run any integration you need. The setup covered in this guide — Docker, PostgreSQL, Nginx reverse proxy, Let's Encrypt SSL, automated backups, and security hardening — is the same stack used in production environments and gives you a stable, secure foundation to build on.
Unlike a minimal Docker run-command install, this setup is built to survive server reboots, handle concurrent workflow executions, and recover gracefully from failures. Your credentials are encrypted, your data is backed up nightly, and your login endpoint is rate-limited against brute force.
Once your instance is running, explore n8n's built-in nodes for databases, APIs, webhooks, and AI integrations to start automating your workflows immediately. The self-hosted community forum at community.n8n.io is also an excellent resource for workflow ideas, node troubleshooting, and deployment tips.

