How to Run n8n Workflow Automation on a VPS (Self-Hosted Zapier)
Zapier charges $20–$70/month per user and limits you to a fixed number of task executions. n8n (pronounced “n-eight-n”) is the open-source workflow automation platform that connects 400+ apps and services — and when self-hosted on a VPS, it’s completely free with unlimited workflows, unlimited executions, and no per-task pricing.
This guide installs n8n on an Ubuntu VPS using Docker, exposes it via Nginx with SSL, and walks through building your first practical automations.
n8n vs Zapier vs Make: Why Self-Host?
| n8n (self-hosted) | Zapier | Make (Integromat) | |
|---|---|---|---|
| Monthly cost | $0 (VPS cost only) | $20–$70+/user | $9–$29+/month |
| Task/execution limits | Unlimited | 750–100K tasks | 1K–150K ops |
| Data privacy | Your server only | Zapier’s servers | Make’s servers |
| Custom code nodes | ✅ JavaScript/Python | Limited | Limited |
| Integrations | 400+ | 6,000+ | 1,500+ |
| Self-host option | ✅ Free | ❌ | ❌ |
The trade-off: self-hosting requires a VPS and initial setup time. The payoff: zero recurring SaaS costs, complete data privacy, and unlimited automation capacity.
💡 VPS.DO Tip: n8n needs at least 1 GB RAM. A 2 vCPU / 4 GB RAM VPS handles n8n plus other applications comfortably. View Plans →
Step 1: Install Docker and Docker Compose
sudo apt update && sudo apt upgrade -y
sudo apt install docker.io docker-compose nginx certbot python3-certbot-nginx -y
sudo systemctl enable docker
sudo usermod -aG docker $USER
newgrp docker
Step 2: Create n8n Directory and Environment File
mkdir -p /var/apps/n8n
cd /var/apps/n8n
nano .env
# Domain where n8n will be accessible
DOMAIN_NAME=n8n.yourdomain.com
# n8n webhook URL (same as domain)
WEBHOOK_URL=https://n8n.yourdomain.com/
# Database credentials (PostgreSQL for production)
POSTGRES_USER=n8n
POSTGRES_PASSWORD=SecurePostgresPass123!
POSTGRES_DB=n8n
# n8n encryption key (generate with: openssl rand -hex 32)
N8N_ENCRYPTION_KEY=your-32-char-hex-key-here
# Basic auth (optional but recommended)
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=YourAdminPassword!
# Timezone
GENERIC_TIMEZONE=America/New_York
# Enable user management
N8N_USER_MANAGEMENT_DISABLED=false
Step 3: Create Docker Compose File
nano docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16
container_name: n8n-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- n8n_postgres_data:/var/lib/postgresql/data
networks:
- n8n-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
# Database
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
# n8n settings
N8N_HOST: ${DOMAIN_NAME}
N8N_PORT: 5678
N8N_PROTOCOL: https
WEBHOOK_URL: ${WEBHOOK_URL}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
# Auth
N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE}
N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER}
N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD}
# Executions
EXECUTIONS_DATA_PRUNE: true
EXECUTIONS_DATA_MAX_AGE: 168 # Keep 7 days of history
volumes:
- n8n_data:/home/node/.n8n
networks:
- n8n-network
depends_on:
postgres:
condition: service_healthy
volumes:
n8n_postgres_data:
n8n_data:
networks:
n8n-network:
docker compose up -d
docker compose ps # Verify both services are running
Step 4: Configure Nginx and SSL
sudo nano /etc/nginx/sites-available/n8n
server {
listen 80;
server_name n8n.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name n8n.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# n8n needs longer timeouts for webhook processing
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d n8n.yourdomain.com
Open https://n8n.yourdomain.com in your browser. You’ll see the n8n interface. ✅
Step 5: Building Your First Workflows
Workflow 1: GitHub → Telegram notifications
Get a Telegram message every time someone opens a pull request on your repository:
- Trigger: GitHub → Webhook → On Pull Request opened
- Action: Telegram → Send Message
- Message:
🔔 New PR: {{$json.pull_request.title}} by {{$json.pull_request.user.login}}
Workflow 2: Daily report from database → Email
- Trigger: Schedule → Every day at 8 AM
- Action: PostgreSQL → Execute Query (daily stats)
- Action: Gmail → Send Email with query results
Workflow 3: New form submission → CRM + Email + Slack
- Trigger: Webhook (receives form POST data)
- Action: Airtable → Create Record
- Action: Gmail → Send welcome email
- Action: Slack → Post to #leads channel
Workflow 4: RSS feed monitor → Blog post draft
- Trigger: RSS Feed Read → Every hour
- Action: HTTP Request → OpenAI/Ollama API (summarize)
- Action: WordPress → Create Draft Post
Step 6: Using the Code Node
n8n’s Code node executes JavaScript or Python — making it far more powerful than no-code tools:
// Example: Transform and filter data in a Code node
const items = $input.all();
return items
.filter(item => item.json.price > 100) // Filter items
.map(item => ({
json: {
...item.json,
// Add computed fields
price_with_tax: item.json.price * 1.1,
display_name: item.json.name.toUpperCase(),
processed_at: new Date().toISOString()
}
}));
Step 7: Connecting to External Services
Most-used integrations
| Category | Services |
|---|---|
| Communication | Telegram, Slack, Discord, Email, SMS |
| CRM/Sales | HubSpot, Salesforce, Pipedrive, Airtable |
| Databases | PostgreSQL, MySQL, MongoDB, Redis |
| Cloud storage | Google Drive, Dropbox, S3, OneDrive |
| Dev tools | GitHub, GitLab, Jira, Linear |
| AI/ML | OpenAI, Anthropic, Google AI, Ollama |
| E-commerce | Shopify, WooCommerce, Stripe |
| Marketing | Mailchimp, Klaviyo, Typeform |
Using HTTP Request for any API
For services without a native n8n node, the HTTP Request node connects to any REST API:
// HTTP Request node configuration:
Method: POST
URL: https://api.yourservice.com/endpoint
Authentication: Header Auth
Header Name: Authorization
Header Value: Bearer {{ $env.API_KEY }}
Body: {
"data": "{{ $json.someField }}"
}
Step 8: Backups and Maintenance
nano ~/backup-n8n.sh
#!/bin/bash
DATE=$(date +%Y-%m-%d)
BACKUP_DIR="/var/backups/n8n"
mkdir -p $BACKUP_DIR
# Export all workflows via n8n CLI
docker exec n8n n8n export:workflow --all \
--output=/home/node/.n8n/workflows-$DATE.json
# Backup PostgreSQL database
docker exec n8n-postgres pg_dump -U n8n n8n \
| gzip > $BACKUP_DIR/n8n-db-$DATE.sql.gz
# Backup n8n data volume
docker run --rm \
-v n8n_data:/data \
-v $BACKUP_DIR:/backup \
alpine tar czf /backup/n8n-data-$DATE.tar.gz -C /data .
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete
echo "n8n backup complete: $DATE"
chmod +x ~/backup-n8n.sh
crontab -e
# 0 2 * * * /bin/bash /root/backup-n8n.sh
Update n8n
cd /var/apps/n8n
docker compose pull
docker compose up -d
docker compose ps
Final Thoughts
Self-hosted n8n on a VPS replaces hundreds of dollars per month in Zapier or Make subscriptions with a one-time setup on a $20–50/month VPS. Unlimited workflows, unlimited executions, complete data privacy, and the flexibility of custom code nodes make it one of the highest-value tools you can self-host.