How to Self-Host n8n: Workflow Automation Zapier Alternative 2026
TL;DR
n8n (Sustainable Use License, ~51K GitHub stars, TypeScript) is the most powerful self-hosted workflow automation platform. Zapier charges $299/month for business plans with 100K tasks. n8n self-hosted is free, supports 400+ integrations (Slack, Gmail, GitHub, databases, AI APIs), runs complex multi-step workflows with conditions/loops, and lets you add custom JavaScript/Python code nodes anywhere in the flow. The visual editor is excellent and the community has thousands of workflow templates.
Key Takeaways
- n8n: ~51K stars, TypeScript — 400+ integrations, visual workflow builder, code nodes
- Self-hosted = unlimited: No task limits, no per-workflow fees, all features available
- Code anywhere: Add JavaScript or Python nodes to do anything an integration doesn't support
- AI Agent nodes: Built-in LangChain integration for AI agent workflows
- Webhooks: Receive webhooks from any service; expose workflows as APIs
- Import community templates: Thousands of ready-to-use workflows from n8n.io/workflows
n8n vs Zapier vs Activepieces
| Feature | n8n | Zapier | Activepieces |
|---|---|---|---|
| License | Sustainable Use | Proprietary | MIT |
| GitHub Stars | ~51K | — | ~10K |
| Cost (self-hosted) | Free | $299/mo | Free |
| Integrations | 400+ | 6000+ | 100+ |
| Code nodes | Yes (JS + Python) | Limited | Yes (TypeScript) |
| AI/LLM nodes | Yes (LangChain) | Yes | Limited |
| Webhook triggers | Yes | Yes | Yes |
| Complex branching | Yes | Limited | Yes |
| Loop/iteration | Yes | Yes | Limited |
| Custom nodes | Yes | No | Yes |
Part 1: Docker Setup
# docker-compose.yml
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
volumes:
- n8n_data:/home/node/.n8n
environment:
N8N_HOST: "n8n.yourdomain.com"
N8N_PORT: 5678
N8N_PROTOCOL: "https"
WEBHOOK_URL: "https://n8n.yourdomain.com/"
N8N_ENCRYPTION_KEY: "${N8N_ENCRYPTION_KEY}"
# Default user (for initial setup):
N8N_DEFAULT_USER_EMAIL: "${ADMIN_EMAIL}"
N8N_DEFAULT_USER_PASSWORD: "${ADMIN_PASSWORD}"
# Disable user registration (after setup):
N8N_USER_MANAGEMENT_DISABLED: "false"
# Optional: external DB instead of SQLite
DB_TYPE: "postgresdb"
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: "${POSTGRES_PASSWORD}"
# Timezone:
GENERIC_TIMEZONE: "America/Los_Angeles"
TZ: "America/Los_Angeles"
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-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
start_period: 20s
volumes:
n8n_data:
postgres_data:
# .env
N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
ADMIN_EMAIL=admin@yourdomain.com
ADMIN_PASSWORD=your-secure-password
POSTGRES_PASSWORD=your-db-password
docker compose up -d
Visit https://n8n.yourdomain.com — log in with your admin credentials.
Part 2: HTTPS with Caddy
n8n.yourdomain.com {
reverse_proxy localhost:5678
}
Part 3: Core Concepts
Nodes
Each step in a workflow is a node. Node types:
- Trigger nodes: Start the workflow (webhook, schedule, event)
- Action nodes: Do something (send email, create issue, query DB)
- Logic nodes: IF/SWITCH conditions, loops, merge data
- Code nodes: Run JavaScript or Python
Credentials
Credentials are encrypted and stored once — reusable across all workflows:
- Settings → Credentials → Add credential
- Select service (GitHub, Slack, PostgreSQL, etc.)
- Authenticate once → use across all workflows
Workflow activation
Workflows with schedule/webhook triggers must be activated to run automatically:
- Active workflows run when triggered
- Inactive workflows only run manually (for testing)
Part 4: Example Automations
New GitHub issue → Slack notification
Trigger: GitHub — On Issues (event: opened)
Action: Slack — Send message
Channel: #engineering
Message: "New issue in {{ $json.repository.full_name }}: {{ $json.issue.title }}\n{{ $json.issue.html_url }}"
Daily digest email
Trigger: Schedule — Every day at 8 AM
Action: HTTP Request — GET https://yourapi.com/daily-summary
Action: Code node — Format the data as HTML
Action: Gmail — Send Email
To: team@company.com
Subject: Daily Digest {{ $now.format('YYYY-MM-DD') }}
Body: {{ $json.formattedHtml }}
Form submission → database + notification
Trigger: Webhook (POST) — receives form data
Action: PostgreSQL — Insert row
Table: leads
Data: {{ $json.body }}
Action: IF node — Check if email domain is company.com
→ True: Slack notification to #sales-hot-leads
→ False: Add to standard nurture sequence
Action: Gmail — Send confirmation email to submitter
AI content moderation
Trigger: Webhook — receives comment from your app
Action: OpenAI — Chat completion
Model: gpt-4o
Prompt: "Is this comment appropriate? Reply YES or NO:\n{{ $json.comment }}"
Action: IF node — Response contains "NO"
→ True: HTTP Request — DELETE comment via API, notify moderator
→ False: Continue (comment approved)
Part 5: Code Nodes
Add JavaScript or Python anywhere:
// JavaScript code node example:
// Transform data from previous node
const items = $input.all();
return items.map(item => {
const data = item.json;
return {
json: {
fullName: `${data.firstName} ${data.lastName}`,
email: data.email.toLowerCase(),
score: data.views * 0.5 + data.clicks * 2,
tier: data.views > 1000 ? 'premium' : 'standard'
}
};
});
# Python code node example:
import json
from datetime import datetime
items = _input.all()
results = []
for item in items:
data = item.json
results.append({
'json': {
'processed_at': datetime.now().isoformat(),
'word_count': len(data.get('content', '').split()),
'tags': [t.strip() for t in data.get('tags', '').split(',')]
}
})
return results
Part 6: AI Agent Workflows
n8n has built-in LangChain support for AI agents:
Trigger: Chat trigger (or webhook)
Agent node: AI Agent
- Model: OpenAI Chat Model (gpt-4o)
- Tools:
→ Calculator tool
→ HTTP Request tool (search web)
→ Custom tool: Query your PostgreSQL
- Memory: Window Buffer Memory (last 10 messages)
Output: Send response back to user
This creates a conversational agent that can query your database, search the web, and perform calculations.
Part 7: Webhook Workflows
n8n generates unique webhook URLs for each trigger:
# Your workflow webhook URL:
https://n8n.yourdomain.com/webhook/your-unique-id
# Test trigger (inactive workflow):
https://n8n.yourdomain.com/webhook-test/your-unique-id
# Send a test webhook:
curl -X POST https://n8n.yourdomain.com/webhook/your-unique-id \
-H "Content-Type: application/json" \
-d '{"event": "order.created", "orderId": 42, "total": 99.99}'
# With basic auth (if configured on webhook node):
curl -u user:pass -X POST https://n8n.yourdomain.com/webhook/your-unique-id \
-d '{"data": "value"}'
Maintenance
# Update n8n:
docker compose pull
docker compose up -d
# Backup:
tar -czf n8n-backup-$(date +%Y%m%d).tar.gz \
$(docker volume inspect n8n_n8n_data --format '{{.Mountpoint}}')
docker exec n8n-postgres-1 pg_dump -U n8n n8n \
| gzip > n8n-db-$(date +%Y%m%d).sql.gz
# Export all workflows as JSON:
docker exec n8n n8n export:workflow --all --output=/home/node/.n8n/workflows-export.json
# Import workflows:
docker exec n8n n8n import:workflow --input=/home/node/.n8n/workflows-export.json
# Logs:
docker compose logs -f n8n
See all open source automation tools at OSSAlt.com/categories/automation.