TemperStack
Intermediate12 min readUpdated Mar 13, 2026

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

  1. Ubuntu 22.04+ VPS with 2-4GB RAM and 1-2 vCPUs
  2. Domain name pointed to VPS IP
  3. SSH access as root or sudo user
  4. 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.

Related Guides

More n8n Tutorials

Other Tool Tutorials

Ready to get started with n8n?

Put this tutorial into practice. Visit n8n and follow the steps above.

Visit n8n