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
- n8n running at
https://n8n.your-domain.com - Automatic SSL via Traefik
- PostgreSQL database for production reliability
- Persistent workflow storage
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:
- Your domain must be accessible from the internet
- The WEBHOOK_URL must match your domain
- 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
- Verify WEBHOOK_URL matches your domain exactly
- Check firewall allows incoming HTTPS
- Test with curl:
curl -X POST https://n8n.your-domain.com/webhook-test/test - Review n8n logs for incoming requests
Cannot connect to external services
- Some services block datacenter IPs
- Check if the service requires IP whitelisting
- Verify DNS resolution inside container:
docker exec n8n-n8n-1 nslookup api.example.com
Workflows fail silently
- Enable detailed logging: set
N8N_LOG_LEVEL=debug - Check execution history in the n8n UI
- Verify credentials are still valid
High memory usage
- PostgreSQL needs tuning for large workflow history
- Consider pruning old executions
- Increase VPS RAM if running many concurrent workflows
Internal links
- Tutorial: Deploy Docker Compose to VPS
- Tutorial: Self-Host Uptime Kuma
- Comparison: Zapier Alternatives
- ServerCompass: One-click n8n deployment
From across the StoicSoft network
Hand-curated reads on the same topic from sister sites in the StoicSoft family.
1FileTool5 min readWatch Any Folder. Process Files as They Arrive.
The Folder Monitor watches any folder and automatically applies a tool to every new file — compress, convert, strip metadata — in the background, without you having to touch the tool each time.
Read on 1filetool.com
Deploy HandbookCoolify vs Dokploy: Self-Hosted PaaS Comparison
Both let you run your own Heroku. Which one should you pick for your VPS?
Read on deployhandbook.com
