Zapier costs $30/month for 750 task runs. Make is cheaper but still per-task. Both make perfect sense until your automations start firing thousands of times — then the math flips hard. n8n is the open-source workflow tool that ends per-task pricing forever: same visual node-based editor, 400+ integrations, but you host it yourself and pay $7.99–$35.99/month for a VPS regardless of how many tasks run.
This guide deploys n8n with a PostgreSQL backend (not SQLite — Postgres survives restarts and scales), sets up Nginx with HTTPS for incoming webhook URLs, hardens the dashboard behind auth, and configures backups. The architecture, decisions, and gotchas matter more than the install commands — which is why this guide spends time on encryption keys, webhook URLs, and the 'why not SQLite' question that breaks more self-hosted n8n setups than anything else.
- A Linux VPS — Ubuntu 22.04 or Debian 12, 2 GB RAM minimum
- A domain pointed at your VPS (n8n's webhook URLs need real HTTPS)
- Roughly 30 minutes
For a turnkey n8n install with Postgres pre-configured, see our n8n VPS plans — instant deploy, no compose-file editing required.
1. When self-hosting n8n actually pays off
Run the numbers before committing:
| Tasks per month | Zapier | Make | Self-hosted n8n on OliveVPS |
|---|---|---|---|
| Under 500 | Free or $30/mo | Free or $9/mo | $7.99/mo (Starter VPS) |
| 2,000–5,000 | $50–$100/mo | $29/mo | $7.99–$15.99/mo |
| 10,000+ | $150–$300/mo | $50–$200/mo | $15.99–$35.99/mo |
| 100,000+ | $500+/mo | $300+/mo | $35.99/mo (Premium VPS) |
The break-even is around 1,000–2,000 monthly tasks. Below that, Zapier's polish wins. Above that, self-hosted is dramatically cheaper — and you control the data, can run AI-heavy workflows with no per-token markup, and don't worry about platform policy changes.
2. Prepare the VPS
apt update && apt upgrade -y
apt install -y curl ca-certificates gnupg ufw
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
adduser n8n
usermod -aG sudo n8n
rsync --archive --chown=n8n:n8n ~/.ssh /home/n8n
3. Install Docker
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
usermod -aG docker n8n
If you're new to Docker, our Docker setup tutorial covers daemon hardening.
4. Deploy n8n with Postgres
This is the most important section. Most n8n tutorials use SQLite — fast to set up, terrible for production. Workflows get corrupted on power loss, the file locks under load, backups are awkward. Use Postgres from day one.
Switch to the n8n user and create the project directory:
su - n8n
mkdir -p ~/n8n
cd ~/n8n
# Generate a random encryption key — this encrypts credentials at rest
# Save this somewhere safe! Losing it = losing all stored credentials.
ENCRYPTION_KEY=$(openssl rand -hex 32)
echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" > ~/.n8n-secrets
chmod 600 ~/.n8n-secrets
echo "Saved your encryption key to ~/.n8n-secrets — back it up off the VPS!"
Create compose.yaml:
services:
postgres:
image: postgres:16-alpine
container_name: n8n-postgres
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: CHANGE_THIS_TO_STRONG_PASSWORD
POSTGRES_DB: n8n
volumes:
- ./postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 5s
timeout: 5s
retries: 10
n8n:
image: docker.n8n.io/n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "127.0.0.1:5678:5678"
volumes:
- ./n8n-data:/home/node/.n8n
environment:
# Database
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: CHANGE_THIS_TO_STRONG_PASSWORD
# Encryption (use the value you generated above)
N8N_ENCRYPTION_KEY: "PASTE_YOUR_ENCRYPTION_KEY_HERE"
# Public URL — critical for webhook URLs
N8N_HOST: n8n.example.com
N8N_PROTOCOL: https
N8N_PORT: 5678
WEBHOOK_URL: https://n8n.example.com/
# Timezone
GENERIC_TIMEZONE: UTC
TZ: UTC
# Logging
N8N_LOG_LEVEL: info
Edit both POSTGRES_PASSWORD entries to match (use the same strong password), paste your encryption key into N8N_ENCRYPTION_KEY, and replace the domain. Then start:
docker compose up -d
docker compose logs -f
First boot takes ~30 seconds. When you see Editor is now accessible via: http://localhost:5678/, it's up.
5. Nginx reverse proxy + HTTPS
sudo apt install -y nginx certbot python3-certbot-nginx
sudo tee /etc/nginx/sites-available/n8n <<'EOF'
server {
listen 80;
server_name n8n.example.com;
client_max_body_size 16M; # Allow larger webhook payloads
location / {
proxy_pass http://127.0.0.1:5678;
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d n8n.example.com
Visit https://n8n.example.com — n8n's setup wizard greets you. Create the owner account (this is the admin). Pick a strong password.
6. Build your first workflow
Click + Add Workflow. n8n's editor is the visual node graph that made Zapier famous, but more powerful.
Drop a Schedule Trigger node (fires on cron) → HTTP Request node (calls an API) → IF node (branches on condition) → Slack node (posts a message).
Click Execute Workflow to test. Each node lights up green/red showing exactly what data flowed through. Active toggle → it runs on schedule.
For 400+ pre-built integrations: search the node palette. Notable ones: Gmail, Slack, Discord, Telegram, Sheets, Notion, Airtable, Postgres, MySQL, Stripe, OpenAI, Anthropic, HuggingFace, plus generic HTTP/Webhook/Cron for anything that doesn't have a dedicated node.
🐾 Skip the compose-file editing
Our n8n VPS plans deploy with Postgres pre-configured, the encryption key already generated, and HTTPS endpoints provisioned. From zero to first workflow in 5 minutes instead of 30.
See n8n VPS Plans →7. Public webhooks
n8n's killer feature for integrations: every workflow gets a public HTTPS webhook URL. Stripe events, GitHub Actions triggers, Discord bot callbacks — anything that can POST to a URL becomes a workflow trigger.
Drop a Webhook node into a workflow. n8n generates two URLs:
- Test URL — only fires when the workflow editor is "listening" (used during development)
- Production URL — fires whenever the workflow is Active
The URL format is https://n8n.example.com/webhook/<uuid>. This is why we set WEBHOOK_URL correctly in the compose file — n8n needs to know its own public hostname.
If you misconfigure the webhook URL early on and create workflows with the wrong URL embedded, you'll have to manually update each one after the fact. Get this right before you build anything important.
8. Backups and credential security
Two things to back up: the encryption key (which decrypts stored credentials) and the Postgres database (which contains workflows, executions, credentials in encrypted form).
Encryption key first. It's already in ~/.n8n-secrets. Print it, save it to a password manager, mail it to yourself, write it on paper. Losing this key means losing every stored credential.
Postgres backups:
cat > /home/n8n/backup.sh <<'EOF'
#!/bin/bash
set -euo pipefail
cd /home/n8n/n8n
BACKUP_DIR=/home/n8n/backups
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"
# Database dump
docker compose exec -T postgres pg_dump -U n8n n8n | gzip > "$BACKUP_DIR/n8n-db-$TIMESTAMP.sql.gz"
# n8n config + binary data
tar -czf "$BACKUP_DIR/n8n-data-$TIMESTAMP.tar.gz" -C /home/n8n/n8n n8n-data
# Keep last 14 days
find "$BACKUP_DIR" -name '*.gz' -mtime +14 -delete
echo "Backup complete: $TIMESTAMP"
EOF
chmod +x /home/n8n/backup.sh
# Cron daily at 3 AM
crontab -e
# Add:
# 0 3 * * * /home/n8n/backup.sh >> /home/n8n/backup.log 2>&1
Push backups off the VPS — rclone to Backblaze B2 / Wasabi / S3. A backup that lives only on the VPS you're trying to protect against is not a backup.
9. Reference: scaling and licensing
Licensing — the fair-code question
n8n uses the Sustainable Use License — a "fair-code" license. Important constraints:
- ✅ Free for internal business use (run workflows for your company)
- ✅ Free for personal use
- ✅ Free for paying clients if the work is bespoke (an agency running client automations)
- ❌ Not free if you resell n8n as a managed SaaS to multiple customers
Most self-hosters are covered. The restriction targets companies that would white-label n8n and sell it as their own product.
Per-plan capacity
| Plan | Workflows | Daily executions | Notes |
|---|---|---|---|
| Starter ($7.99, 2 GB) | 10–20 | ~5,000 | Light workflows only — no AI nodes |
| Pro ($15.99, 4 GB) | 50–100 | ~50,000 | Production setups with AI integration |
| Premium ($35.99, 8 GB) | 200+ | 500,000+ | High-throughput, multi-user teams |
AI workflows are RAM-heavy
OpenAI, Anthropic, and HuggingFace nodes load large payloads in memory. A single GPT-4-class workflow can consume 200–500 MB transiently. Plan for Pro or Premium if your automations are AI-heavy.
Queue mode for high throughput
Default n8n runs all workflows in the main process. For high-volume use (10,000+ daily executions), enable queue mode: add a Redis container and set EXECUTIONS_MODE=queue. Workers process the queue in parallel. Documented in detail in the n8n docs — covered briefly here because it's only worth it at real scale.
External Postgres for production
The compose file above runs Postgres alongside n8n. Fine for small setups. For real production, run Postgres on a separate VPS — survives Docker restarts independently, can be scaled separately. See our PostgreSQL setup guide for the standalone version.
FAQ
n8n vs Make vs Zapier — which one wins?
Zapier wins for ease (drag, drop, done — no infra). Make wins for complex branching and visual workflows. n8n wins when self-hosting matters: full data sovereignty, unlimited executions, no per-task pricing. The break-even is around 1,000–2,000 monthly tasks. Below that, Zapier is easier. Above that, n8n pays for itself within a month.
Can I import workflows from Zapier or Make?
No direct import. The workflow data formats are different. But the concepts translate — Zaps and Scenarios become workflows, triggers become Trigger nodes, actions become regular nodes. Most users rebuild rather than migrate, and it usually takes 30 minutes per non-trivial workflow.
How do I run AI-heavy workflows reliably?
The Pro plan minimum, ideally Premium for serious GPT-4/Claude usage. AI nodes are RAM-heavy. Enable queue mode (Redis + worker container) so AI calls don't block the main process. Also: set rate limits on the API node — burning through OpenAI's TPM limits makes everything fail.
Is the SQLite default really that bad?
For development and learning, SQLite is fine. For production with concurrent workflows, regular webhook traffic, or anything you care about — yes, it's that bad. SQLite locks aggressively under writes, corrupts on power loss, and is awkward to back up. Use Postgres from day one.
Can multiple team members use one n8n install?
Yes — n8n has built-in multi-user support with role-based access (Owner / Admin / Member). Each user has their own workflows; shared credentials work across users with permissions. The Starter plan handles small teams (~5 users); Pro is comfortable for 10–20 active users.
What happens if I lose my encryption key?
Every stored credential becomes unreadable. The workflows still load (their structure isn't encrypted) but they can't authenticate to external services. You'll need to re-enter every API key, OAuth token, and password manually. Back up the encryption key separately from the database — they should never sit in the same backup blob.