SELF-HOST, WITHOUT THE GUESSWORK

How to Self-Host n8n on a VPS (Production Setup)

Run n8n workflow automation on your own VPS. Build integrations without per-execution pricing.

How to Self-Host n8n on a VPS (Production Setup)

n8n is a workflow automation tool that connects your apps and services. Self-hosting means unlimited executions at flat server cost.

The managed version charges per execution. If you run many workflows, self-hosting makes financial sense.

What you will have at the end

Step 1: Prepare the server

sudo apt update && sudo apt upgrade -y

Install Docker if needed:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker

Step 2: Create directory structure

mkdir -p ~/apps/n8n/data
mkdir -p ~/apps/n8n/files
cd ~/apps/n8n

Step 3: Create docker-compose.yml

services:
  traefik:
    image: traefik:v2.11
    command:
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
      - [email protected]
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt:/letsencrypt
    restart: unless-stopped

  postgres:
    image: postgres:15-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: n8nio/n8n:latest
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
      - N8N_HOST=n8n.your-domain.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.your-domain.com/
      - GENERIC_TIMEZONE=UTC
    volumes:
      - ./data:/home/node/.n8n
      - ./files:/files
    labels:
      - traefik.enable=true
      - traefik.http.routers.n8n.rule=Host(`n8n.your-domain.com`)
      - traefik.http.routers.n8n.entrypoints=websecure
      - traefik.http.routers.n8n.tls=true
      - traefik.http.routers.n8n.tls.certresolver=letsencrypt
      - traefik.http.services.n8n.loadbalancer.server.port=5678

volumes:
  letsencrypt:
  postgres_data:

Step 4: Configure environment variables

Create .env file:

# Database password
POSTGRES_PASSWORD=$(openssl rand -hex 16)

# n8n admin credentials
N8N_USER=admin
N8N_PASSWORD=$(openssl rand -base64 24)

# Save to .env
cat > .env << EOF
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
N8N_USER=$N8N_USER
N8N_PASSWORD=$N8N_PASSWORD
EOF

echo "Your n8n credentials:"
echo "User: $N8N_USER"
echo "Password: $N8N_PASSWORD"

Save these credentials securely.

Step 5: Deploy

docker compose up -d

Wait for PostgreSQL to initialize before n8n starts.

Step 6: Verify installation

docker compose ps
docker compose logs -f n8n

Visit https://n8n.your-domain.com and log in with your credentials.

Step 7: Configure webhooks

For webhook triggers to work:

  1. Your domain must be accessible from the internet
  2. The WEBHOOK_URL must match your domain
  3. Firewall must allow incoming connections on 443

Test with a simple webhook workflow to verify.

Backup strategy

n8n with PostgreSQL needs database backups and workflow exports.

#!/bin/bash
# Daily backup script
BACKUP_DIR=/backups/n8n/$(date +%Y%m%d)
mkdir -p $BACKUP_DIR

# Postgres dump
docker exec n8n-postgres-1 pg_dump -U n8n n8n > $BACKUP_DIR/n8n.sql

# Backup local files
tar -czf $BACKUP_DIR/n8n-data.tar.gz ~/apps/n8n/data

# Compress
tar -czf $BACKUP_DIR.tar.gz $BACKUP_DIR

Also consider exporting workflows as JSON from the n8n UI for version control.

Troubleshooting

Webhooks not triggering

Cannot connect to external services

Workflows fail silently

High memory usage

Internal links

From across the StoicSoft network

Hand-curated reads on the same topic from sister sites in the StoicSoft family.