How to Self-Host n8n: Zapier Alternative for Workflow Automation 2026
TL;DR
n8n is a visual workflow automation platform with 50K GitHub stars. It connects 400+ services, runs JavaScript/Python code inline, and handles webhooks, cron schedules, and event-based triggers. Self-hosted n8n runs unlimited workflows for server cost only ($6/month). Zapier charges $20–$50+/month for limited tasks. For developers who want automation power without task/run limits: n8n is the answer.
Key Takeaways
- n8n: ~50K GitHub stars, TypeScript, Sustainable Use License (free for self-hosting), 400+ nodes
- Zapier comparison: n8n has no task limits when self-hosted; Zapier charges per task
- Code nodes: Run JavaScript or Python inline — not possible in Zapier/Make
- Webhook triggers: Instant HTTP webhooks, no polling delays
- Setup: Docker + SQLite (default) or Postgres — 10 minutes
- Use cases: API integrations, data pipelines, DevOps automation, notifications, ETL
n8n vs Zapier vs Make
| Feature | n8n (self-hosted) | Zapier | Make (Integromat) |
|---|---|---|---|
| Cost | ~$6/month (server) | $20–600+/month | $9–100+/month |
| Task limits | None (self-hosted) | 750–2M tasks/month | 1K–800K ops/month |
| Code execution | ✅ JavaScript + Python | ❌ | Limited |
| Custom HTTP calls | ✅ | ✅ | ✅ |
| Webhook triggers | ✅ | ✅ | ✅ |
| Integrations | 400+ | 6,000+ | 1,500+ |
| Visual builder | ✅ | ✅ | ✅ |
| Air-gapped deployment | ✅ | ❌ | ❌ |
| Open source | Partial (SUL) | No | No |
When Zapier/Make beats n8n: If you need 6,000+ native integrations and don't want to self-host. n8n's HTTP Request node covers any API without a dedicated node, but requires more setup.
Server Requirements
- Minimum: 512MB RAM (SQLite backend, few workflows)
- Recommended: 1–2GB RAM with Postgres (production workloads)
- VPS: Hetzner CX22 (€4.35/month) works fine
Part 1: Docker Setup
Option A: Quick Start (SQLite — Personal Use)
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-v ~/.n8n:/home/node/.n8n \
n8nio/n8n
Option B: Docker Compose with PostgreSQL (Production)
# docker-compose.yml
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
# Required:
N8N_HOST: "n8n.yourdomain.com"
N8N_PORT: 5678
N8N_PROTOCOL: "https"
WEBHOOK_URL: "https://n8n.yourdomain.com/"
# Database:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: "${POSTGRES_PASSWORD}"
# Security:
N8N_BASIC_AUTH_ACTIVE: "true"
N8N_BASIC_AUTH_USER: "${N8N_USERNAME}"
N8N_BASIC_AUTH_PASSWORD: "${N8N_PASSWORD}"
# Execution:
EXECUTIONS_DATA_SAVE_ON_SUCCESS: all
EXECUTIONS_DATA_SAVE_ON_ERROR: all
EXECUTIONS_DATA_MAX_AGE: 336 # Keep 14 days
# Email for notifications:
N8N_EMAIL_MODE: smtp
N8N_SMTP_HOST: "${SMTP_HOST}"
N8N_SMTP_PORT: 587
N8N_SMTP_USER: "${SMTP_USER}"
N8N_SMTP_PASS: "${SMTP_PASSWORD}"
N8N_SMTP_SENDER: "n8n@yourdomain.com"
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
n8n_data:
postgres_data:
# .env
POSTGRES_PASSWORD=strong-password-here
N8N_USERNAME=admin
N8N_PASSWORD=your-n8n-password
SMTP_HOST=smtp.yourdomain.com
SMTP_USER=noreply@yourdomain.com
SMTP_PASSWORD=smtp-password
docker compose up -d
Part 2: HTTPS with Caddy
n8n.yourdomain.com {
reverse_proxy localhost:5678
# WebSocket support for real-time updates:
@websocket {
header Connection *Upgrade*
header Upgrade websocket
}
handle @websocket {
reverse_proxy localhost:5678
}
}
Part 3: Your First Workflow
Visit https://n8n.yourdomain.com and log in.
Example: Webhook → HTTP Request → Slack Notification
This workflow: receives a webhook, calls an API, and sends results to Slack.
-
Add a Webhook node:
- Click + → Search "Webhook"
- Method: GET or POST
- Copy the webhook URL shown:
https://n8n.yourdomain.com/webhook/your-unique-id
-
Add HTTP Request node (connect to Webhook):
- URL:
https://api.github.com/repos/{{$json.repo}}/issues - Authentication: None (or add GitHub token for private repos)
- Method: GET
- URL:
-
Add Slack node (connect to HTTP Request):
- Add your Slack OAuth credential
- Channel:
#alerts - Message:
{{ $json.length }} new issues in {{ $node.Webhook.json.repo }}
-
Activate the workflow (toggle in top right)
Test: Send a POST to your webhook URL:
curl -X POST https://n8n.yourdomain.com/webhook/your-id \
-H "Content-Type: application/json" \
-d '{"repo": "your-org/your-repo"}'
Part 4: Schedule-Based Workflows
Run workflows on a cron schedule:
- Add Schedule Trigger node instead of Webhook
- Set interval: Every day at 9am, every hour, every Monday, etc.
- Use cron expression for custom schedules:
0 9 * * 1-5(9am weekdays)
Example: Daily Slack digest of GitHub PRs:
Schedule (9am daily)
→ HTTP Request (GitHub API: list open PRs)
→ Code (format PR list as message)
→ Slack (post to #dev-standup)
Part 5: Code Node (JavaScript / Python)
The Code node is n8n's superpower — execute arbitrary code inline:
// Transform and filter data using JavaScript:
const items = $input.all();
return items
.filter(item => item.json.status === 'open')
.map(item => ({
json: {
id: item.json.id,
title: item.json.title,
url: `https://github.com/issues/${item.json.number}`,
age_days: Math.floor(
(Date.now() - new Date(item.json.created_at)) / 86400000
),
}
}));
# Python code node:
issues = [item["json"] for item in _input.all()]
stale = [i for i in issues if i.get("age_days", 0) > 14]
return [{"json": i} for i in stale]
Part 6: Common Workflow Patterns
API Webhook → Database
Webhook (receive form submission)
→ Set (extract relevant fields)
→ Postgres (INSERT INTO leads ...)
→ Email (send confirmation)
GitHub Actions Notification
Webhook (GitHub Actions webhook)
→ If (check job.status == 'failure')
→ Slack (notify #ci-alerts: "Build failed: {{ $json.workflow }}")
Daily Report
Schedule (6am daily)
→ HTTP Request (fetch yesterday's data from your API)
→ Code (calculate metrics, format report)
→ Email (send to team@company.com)
File Processing
FTP Trigger (new file detected)
→ Read Binary File
→ HTTP Request (upload to S3)
→ Postgres (log upload record)
→ Slack (notify #uploads)
Part 7: Sharing and Credentials
Credential Management
n8n stores credentials encrypted. Add them once, reuse across workflows:
- Credentials → New → Select service (Slack, GitHub, Postgres, etc.)
- Enter OAuth tokens or API keys
- Credentials are encrypted in the database — never visible in workflow JSON
Export and Import Workflows
# Export all workflows:
n8n export:workflow --all --output=workflows-backup.json
# Import:
n8n import:workflow --input=workflows-backup.json
Or use the UI: Workflows → Export / Import.
Part 8: Scaling
Queue Mode (Multiple Workers)
For high-volume workflows:
environment:
EXECUTIONS_MODE: queue
QUEUE_BULL_REDIS_HOST: redis
QUEUE_BULL_REDIS_PORT: 6379
Add Redis and multiple n8n worker instances for parallel execution.
Concurrency Limits
environment:
N8N_CONCURRENCY_PRODUCTION_LIMIT: 10 # Max parallel executions
Maintenance
# Update n8n:
docker compose pull
docker compose up -d
# Check execution logs:
docker compose logs -f n8n
# Backup database:
docker exec n8n-postgres pg_dump -U n8n n8n | \
gzip > n8n-backup-$(date +%Y%m%d).sql.gz
Cost Comparison
| Service | Monthly Cost | Task/Run Limits |
|---|---|---|
| Zapier Starter | $20/month | 750 tasks |
| Zapier Professional | $50/month | 2,000 tasks |
| Make Core | $9/month | 10,000 ops |
| n8n self-hosted | ~$6/month | Unlimited |
| n8n Cloud (managed) | $20/month | 2,500 executions |
Compare all open source Zapier alternatives at OSSAlt.com/alternatives/zapier.