Skip to main content

How to Self-Host n8n: Workflow Automation Zapier Alternative 2026

·OSSAlt Team
n8nautomationzapierself-hostingdockerworkflows2026

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

Featuren8nZapierActivepieces
LicenseSustainable UseProprietaryMIT
GitHub Stars~51K~10K
Cost (self-hosted)Free$299/moFree
Integrations400+6000+100+
Code nodesYes (JS + Python)LimitedYes (TypeScript)
AI/LLM nodesYes (LangChain)YesLimited
Webhook triggersYesYesYes
Complex branchingYesLimitedYes
Loop/iterationYesYesLimited
Custom nodesYesNoYes

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:

  1. Settings → Credentials → Add credential
  2. Select service (GitHub, Slack, PostgreSQL, etc.)
  3. 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.

Comments