How to self-host n8n on a VPS on n8n
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
Step-by-Step Instructions
Connect to VPS and Update System
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.Install Docker and Docker Compose
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.Create Project Directory
mkdir ~/n8n && cd ~/n8n. This will hold your configuration files.Create .env Configuration File
.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.Create docker-compose.yml
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:Start the n8n Stack
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.Set Up Reverse Proxy for HTTPS
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.Verify Installation and Webhooks
Update and Maintain
docker compose pull then docker compose up -d. Backup volumes regularly: docker volume ls and use provider snapshots.Common Issues & Troubleshooting
Port conflicts or Docker not starting
Check running services with <code>sudo netstat -tuln | grep 5678</code>, kill conflicts, ensure Docker service is active: <code>sudo systemctl status docker</code>.
Volume permission errors
Fix ownership: <code>sudo chown -R $USER:$USER ~/n8n</code>, or run Docker as root temporarily.
Webhook URLs show localhost
Set <code>WEBHOOK_URL=https://n8n.yourdomain.com/</code> in .env and restart: <code>docker compose down && docker compose up -d</code>.
Postgres connection fails
Verify env vars match, check health: <code>docker compose logs postgres</code>, ensure depends_on with healthcheck.
Can't access after HTTPS setup
Check Nginx config syntax: <code>sudo nginx -t</code>, reload: <code>sudo systemctl reload nginx</code>, verify Certbot and firewall.