Intermediate
How to self-host n8n on a VPS using Docker
Quick Answer
Self-host n8n on a VPS using Docker Compose with Postgres for production reliability. Set up in 30-60 minutes on Ubuntu by installing Docker, configuring .env and docker-compose.yml, then adding a reverse proxy for HTTPS. Test webhooks to confirm readiness.
Prerequisites
- Ubuntu 22.04+ VPS with 2-4GB RAM and 1-2 vCPUs
- Domain name pointed to VPS IP
- SSH access as root or sudo user
- Basic Linux and Docker knowledge
1
Connect to VPS and Update System
SSH into your VPS as root:
ssh root@your-vps-ip-address. Update packages with apt update && apt upgrade -y. For security, create a non-root user: adduser n8nuser, usermod -aG sudo n8nuser, then su - n8nuser.Tip
Use key-based SSH authentication instead of passwords for better security.
2
Install Docker and Docker Compose
Install Docker:
sudo apt install docker.io -y, start and enable it with sudo systemctl start docker and sudo systemctl enable docker, add user to docker group: sudo usermod -aG docker $USER. Install Compose plugin: sudo apt install docker-compose-plugin -y. Log out and SSH back in to apply group changes.Tip
Verify with
docker --version and docker compose version.3
Create Project Directory
Create and enter the n8n directory:
mkdir ~/n8n && cd ~/n8n. This will hold your configuration files.4
Create .env Configuration File
Use nano or vim to create
.env. Add these settings (generate encryption key with openssl rand -base64 32): N8N_HOST=n8n.yourdomain.com
N8N_PORT=5678
WEBHOOK_URL=https://n8n.yourdomain.com/
N8N_PROTOCOL=https
GENERIC_TIMEZONE=UTC
TZ=UTC
N8N_ENCRYPTION_KEY=your-32-char-random-key-here
DB_TYPE=postgresdb
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=your-secure-db-password
DB_POSTGRESDB_SCHEMA=public
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=your-secure-auth-password Replace placeholders with your values.Tip
Keep the .env file secure and back it up; it contains sensitive keys and passwords.
5
Create docker-compose.yml
Create
docker-compose.yml with this production stack: version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: n8n-postgres
restart: always
environment:
- POSTGRES_USER=${DB_POSTGRESDB_USER}
- POSTGRES_PASSWORD=${DB_POSTGRESDB_PASSWORD}
- POSTGRES_DB=${DB_POSTGRESDB_DATABASE}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- n8n-network
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${DB_POSTGRESDB_USER} -d ${DB_POSTGRESDB_DATABASE}']
interval: 5s
timeout: 5s
retries: 5
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: always
ports:
- '5678:5678'
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_DATABASE=${DB_POSTGRESDB_DATABASE}
- DB_POSTGRESDB_HOST=${DB_POSTGRESDB_HOST}
- DB_POSTGRESDB_PORT=${DB_POSTGRESDB_PORT}
- DB_POSTGRESDB_USER=${DB_POSTGRESDB_USER}
- DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
- DB_POSTGRESDB_SCHEMA=${DB_POSTGRESDB_SCHEMA}
- N8N_HOST=${N8N_HOST}
- N8N_PORT=${N8N_PORT}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- WEBHOOK_URL=${WEBHOOK_URL}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE}
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
networks:
- n8n-network
volumes:
postgres_data:
n8n_data:
networks:
n8n-network:Tip
This includes healthchecks and volumes for persistence.
6
Start the n8n Stack
Run
docker compose up -d to start in detached mode. Check logs with docker compose logs -f and status with docker ps. Access temporarily at http://your-vps-ip:5678 to set up owner account.Tip
Use
docker compose down to stop safely.7
Set Up Reverse Proxy for HTTPS
Install Nginx:
sudo apt install nginx -y. Configure a site for your domain, enable HTTPS with Certbot: sudo apt install certbot python3-certbot-nginx -y, then sudo certbot --nginx -d n8n.yourdomain.com. Proxy traffic from port 443 to n8n's 5678.Tip
Example Nginx config: server block with
proxy_pass http://localhost:5678;.8
Verify Installation and Webhooks
Access https://n8n.yourdomain.com. Create a test workflow with a Webhook node, switch to Production URL, and test with Postman. Ensure URL starts with your domain, not localhost.
Tip
Check firewall:
sudo ufw allow 80,443 and sudo ufw enable.9
Update and Maintain
To update:
docker compose pull then docker compose up -d. Backup volumes regularly: docker volume ls and use provider snapshots.Troubleshooting
Port conflicts or Docker not starting
Check running services with
sudo netstat -tuln | grep 5678, kill conflicts, ensure Docker service is active: sudo systemctl status docker.Volume permission errors
Fix ownership:
sudo chown -R $USER:$USER ~/n8n, or run Docker as root temporarily.Webhook URLs show localhost
Set
WEBHOOK_URL=https://n8n.yourdomain.com/ in .env and restart: docker compose down && docker compose up -d.Postgres connection fails
Verify env vars match, check health:
docker compose logs postgres, ensure depends_on with healthcheck.Can't access after HTTPS setup
Check Nginx config syntax:
sudo nginx -t, reload: sudo systemctl reload nginx, verify Certbot and firewall.Ready to get started with n8n?
Put this tutorial into practice. Visit n8n and follow the steps above.
Visit n8n →