Skip to main content

Open-source alternatives guide

How to Self-Host n8n Workflow Automation 2026

Self-host n8n in 2026. Sustainable Use License, ~51K stars, TypeScript — visual workflow automation with 400+ integrations. Docker setup, webhooks now.

·OSSAlt Team
Share:

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

Why Self-Host n8n

The cost difference between n8n self-hosted and Zapier is staggering. Zapier's Starter plan ($29.99/month, 750 tasks) gets exhausted quickly by active automations. Their Professional plan ($73.50/month, 2,000 tasks) is the minimum for a team with meaningful automation needs. The Business plan at $299/month covers 100,000 tasks. A busy engineering team can hit 100,000 tasks in a month with just a handful of active workflows (GitHub webhooks, Slack notifications, scheduled jobs).

n8n self-hosted costs the price of a VPS — $10-20/month for a 2 vCPU/4GB RAM machine that runs unlimited workflows with unlimited executions. One medium-complexity workflow on Zapier's Business plan might cost $50-100/month equivalent in task consumption. A dozen such workflows and you're at the $1,200/year Zapier bill versus $180/year for a VPS.

Beyond cost, n8n's code nodes are the feature that separates it from most automation tools. When an integration doesn't support exactly what you need, a code node lets you write JavaScript or Python to transform data, call APIs with custom headers, parse unusual formats, or implement complex business logic. Most Zapier workflows either need a "Code" step that's limited and poorly documented, or require building a full serverless function just to do a data transformation. n8n's code nodes are first-class citizens.

The AI integration story is also compelling. n8n's built-in LangChain support lets you build AI agent workflows that combine language models with real actions — query your database, send a Slack message, update a CRM record. This is the kind of automation that previously required custom engineering work.

When NOT to self-host n8n: n8n's Sustainable Use License means you can self-host for free, but building products that embed n8n for customers requires a commercial license. Read the license carefully for commercial use cases. Also, n8n's 400+ integrations is fewer than Zapier's 6,000+ — if you need a niche integration that n8n doesn't support, check before committing.

Prerequisites

n8n with PostgreSQL requires a server capable of running both the application and the database reliably.

Server specs: 2 vCPUs and 2GB RAM is the comfortable minimum for n8n plus PostgreSQL. Workflow executions are CPU-intensive if you have many concurrent runs or complex code nodes. For teams running automations continuously, 4 vCPUs and 4GB RAM gives you comfortable headroom. Check our VPS comparison for self-hosters — the $10-15/month tier from Hetzner or Contabo fits n8n well.

PostgreSQL vs SQLite: n8n ships with SQLite by default, but for production use, PostgreSQL is strongly recommended. SQLite works fine for light use, but it can't handle concurrent workflow executions reliably and doesn't support the full execution history features. The Docker Compose setup in Part 1 uses PostgreSQL.

Domain and HTTPS: n8n's webhook functionality requires a public HTTPS URL. Webhooks from external services (GitHub, Stripe, etc.) can't reach a server without a valid SSL certificate. Your domain needs to be publicly accessible and HTTPS needs to work before activating webhook-triggered workflows.

Encryption key: The N8N_ENCRYPTION_KEY is used to encrypt stored credentials (API keys, OAuth tokens). This key must be 32+ bytes and must never change after initial setup — changing it invalidates all stored credentials and you'll need to re-enter every API key.

Operating system: Ubuntu 22.04 LTS. n8n's Node.js runtime and PostgreSQL both run excellently on Ubuntu, and the Docker images are tested against this environment.

Skill level: Intermediate. n8n's visual editor is beginner-friendly, but server setup and troubleshooting require Docker and Linux familiarity.

Production Security Hardening

n8n stores credentials for every service it connects to — Slack tokens, GitHub API keys, database passwords, email credentials. This makes it a high-value target. Follow the self-hosting security checklist and these n8n-specific steps:

Firewall (UFW): Never expose port 5678 directly. Only allow HTTP/HTTPS through your reverse proxy.

sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Disable user registration: By default, n8n allows new user registration. After setup, set N8N_USER_MANAGEMENT_JWT_SECRET and configure access control in Settings. For a single-user or small-team setup, consider setting N8N_BASIC_AUTH_ACTIVE: "true" as an additional layer.

Secrets management: The N8N_ENCRYPTION_KEY and database password are critical. Store them in .env:

# .env (never commit this)
N8N_ENCRYPTION_KEY=your-64-char-hex-key
POSTGRES_PASSWORD=your-db-password
ADMIN_PASSWORD=your-admin-password
echo ".env" >> .gitignore

Webhook security: Production webhooks should use authentication. Configure the webhook node with Header Auth or Basic Auth to verify the sender. This prevents random internet traffic from triggering your workflows.

Disable SSH password authentication: Edit /etc/ssh/sshd_config: set PasswordAuthentication no and PermitRootLogin no. Restart: sudo systemctl restart ssh.

Automatic security updates:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades

Regular backups: n8n's database contains all your workflows, credentials, and execution history. Back up both the n8n data volume and PostgreSQL database. See automated server backups with restic for automated off-site backup that handles both Docker volumes and database dumps.

Workflow Design Principles

n8n's flexibility is its greatest strength and biggest footgun. Without discipline, you end up with dozens of undocumented workflows that nobody understands, credentials that expired last year, and automations that silently stopped working months ago.

Name workflows descriptively from the start. "Webhook 3" is useless after a week. "GitHub PR → Slack notify #engineering" tells you immediately what it does, where data comes from, and where it goes. Use a consistent naming format: [Trigger source] → [Action type] [Destination]. This makes the workflow list scannable when you have 50+ workflows.

Add sticky notes to every workflow. n8n supports sticky note nodes that render as visual annotations in the canvas. Use them to document: why this workflow exists, what system it integrates, any non-obvious configuration decisions, and when it was last reviewed. Workflows without documentation become unmaintainable as soon as the person who built them leaves or forgets the context.

Test workflows in inactive mode before activating them. n8n provides separate webhook URLs for testing (/webhook-test/) versus production (/webhook/). Always test with real data in inactive mode, verify the output at each step, and only activate once you're confident the workflow behaves correctly. Activating untested workflows in production causes hard-to-diagnose issues when real data triggers unexpected code paths.

Build error handling explicitly. n8n's default behavior on node failure is to stop execution and mark the run as failed. For production workflows, add error branches using the "Error Trigger" workflow feature — n8n lets you designate a workflow that runs whenever another workflow fails, letting you send Slack alerts or log errors to a database. Without error handling, failures are silent.

Credential hygiene requires regular maintenance. OAuth2 tokens expire, API keys rotate, and services change their authentication requirements. Schedule a quarterly review of all credentials in n8n: test each one, rotate anything older than 6 months, and delete credentials for services you no longer use. An expired credential that silently fails is worse than one that was never set up — it looks like the workflow is working until you check the execution log.

Troubleshooting Common Issues

Webhooks trigger locally but fail from external services

The external service (GitHub, Stripe, etc.) can't reach your n8n server. Verify the WEBHOOK_URL in your .env is your public HTTPS URL, not localhost. Test from outside your network: curl -X POST https://n8n.yourdomain.com/webhook/test-id. Check that port 443 is open in your firewall and that SSL is working. Many external services also have IP allowlist requirements.

Credentials marked "invalid" after server restart

This means N8N_ENCRYPTION_KEY changed between restarts. The encryption key must be stable. If you're generating it dynamically in .env with $(openssl rand -hex 32), that generates a new key every time the environment is evaluated. Set a fixed value and keep it safe. If credentials are already invalid, you'll need to re-enter them all.

Workflow executions stuck in "running" or "waiting"

A workflow can get stuck if a node is waiting for a response that never comes (webhook, HTTP timeout) or if n8n crashed during execution. Check the execution log: n8n → Executions → find the stuck execution. You can stop it manually. Increase node timeouts for slow external services. If n8n itself is unresponsive, restart the container: docker compose restart n8n.

PostgreSQL connection errors after starting

n8n depends on PostgreSQL being fully ready. The healthcheck in the Docker Compose config handles this, but if PostgreSQL is slow to start (common on first run), n8n may timeout. Wait 30-60 seconds after docker compose up -d before accessing n8n. Check docker compose logs postgres to see if PostgreSQL is running.

Code nodes fail with "cannot find module"

n8n's code nodes run in a sandboxed Node.js environment and don't have access to npm packages by default. You can use standard JavaScript built-ins and the $ n8n variables, but require('lodash') won't work. For complex data manipulation, use the available built-in methods or restructure your logic to avoid external dependencies.

See all open source automation tools at OSSAlt.com/categories/automation.

See open source alternatives to n8n on OSSAlt.

The SaaS-to-Self-Hosted Migration Guide (Free PDF)

Step-by-step: infrastructure setup, data migration, backups, and security for 15+ common SaaS replacements. Used by 300+ developers.

Join 300+ self-hosters. Unsubscribe in one click.