Skip to main content
Back to Category

n8n Setup: Self-Hosted Workflow Automation

n8n Docker installation, webhook integrations, workflow creation, 400+ app connections, scheduled tasks, credentials management and production deployment.

Read time: 15 min Self-Hosting
n8nautomationworkflowself-hostingdockerwebhookintegration

Table of Contents

n8n Setup: Self-Hosted Workflow Automation

n8n is an open-source workflow automation platform with a visual interface that can connect 400+ applications and services. You can run it on your own server as a self-hosted alternative to services like Zapier and Make (Integromat). You can create complex automations without writing code, or add custom logic with JavaScript/Python.

What is n8n?

n8n (short for "nodemation") lets you build workflows with node-based visual programming. Key features:

  • 400+ Integrations: Slack, GitHub, Google Sheets, Notion, Airtable and more
  • Webhook Support: Use HTTP requests as triggers
  • Scheduled Tasks: Cron-based scheduling
  • Code Support: JavaScript and Python nodes
  • Error Handling: Retry mechanism and error flows
  • Encrypted Credentials: API keys stored securely

System Requirements

  • Server with Docker and Docker Compose installed
  • Minimum 1 GB RAM (2 GB recommended)
  • PostgreSQL (recommended for production) or SQLite

Basic Docker Installation

Quick Start with SQLite

hljs yaml
# docker-compose.yml
version: '3.8'

services:
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    environment:
      - N8N_HOST=n8n.example.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - WEBHOOK_URL=https://n8n.example.com/
      - GENERIC_TIMEZONE=Europe/London
      - N8N_ENCRYPTION_KEY=strong-encryption-key
    volumes:
      - ./n8n-data:/home/node/.n8n
    ports:
      - "127.0.0.1:5678:5678"
hljs bash
# Start the container
docker compose up -d

# Check logs
docker logs n8n -f

N8N_ENCRYPTION_KEY is used to encrypt credentials. If you lose this value, all your credentials become inaccessible. Store it in a safe place.

Production Setup with PostgreSQL

hljs yaml
version: '3.8'

services:
  postgres:
    image: postgres:15
    container_name: n8n-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: n8n
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: strong-password
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 5s
      timeout: 5s
      retries: 10

  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=strong-password
      - N8N_HOST=n8n.example.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.example.com/
      - GENERIC_TIMEZONE=Europe/London
      - N8N_ENCRYPTION_KEY=strong-encryption-key
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=admin-password
    volumes:
      - ./n8n-data:/home/node/.n8n
    ports:
      - "127.0.0.1:5678:5678"

volumes:
  postgres-data:

Nginx Reverse Proxy

hljs nginx
server {
    listen 80;
    server_name n8n.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name n8n.example.com;

    ssl_certificate /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;

    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;

        # WebSocket support (required for n8n)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600;
    }
}

Creating Basic Workflows

First Workflow: Webhook → Slack Notification

  1. Click New Workflow
  2. Add node with + → select Webhook
    • HTTP Method: POST
    • Path: /slack-notify
  3. Add new node with + → select Slack
    • Operation: Send Message
    • Channel: #general
    • Message: {{ $json.message }}
  4. Save and Activate
hljs bash
# Test the webhook
curl -X POST https://n8n.example.com/webhook/slack-notify \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello n8n!"}'

Trigger Types

Webhook Trigger

hljs javascript
// Webhook node settings
{
  "httpMethod": "POST",
  "path": "my-webhook",
  "responseMode": "onReceived",
  "responseData": "allEntries"
}

Cron (Schedule) Trigger

# Cron expressions
0 9 * * 1-5    # Weekdays at 09:00
*/15 * * * *   # Every 15 minutes
0 0 * * *      # Every midnight
0 8 * * 1      # Every Monday at 08:00

Credentials Management

Settings → Credentials → Add Credential

Example: GitHub API
- Credential Type: GitHub API
- Access Token: ghp_xxxxxxxxxxxx
- Test the connection

GitHub → Slack Notification

GitHub Trigger (push event)
  → IF (branch == 'main')
    → Slack ("New deploy: {{ $json.commits[0].message }}")
    → Email (notify team)

Daily Report

Cron Trigger (every morning at 09:00)
  → PostgreSQL (fetch yesterday's sales)
  → Code (format data)
  → Google Sheets (add to report)
  → Slack (send summary)

Form → CRM Integration

Webhook (form submit)
  → Validate (required fields)
  → HubSpot (create contact)
  → Email (welcome email)
  → Slack (notify sales team)

Queue Mode (High Load)

Use queue mode for high workloads in production:

hljs yaml
environment:
  - EXECUTIONS_MODE=queue
  - QUEUE_BULL_REDIS_HOST=redis
  - QUEUE_BULL_REDIS_PORT=6379

# Add Redis service
redis:
  image: redis:7-alpine
  container_name: n8n-redis
  restart: unless-stopped

Backup

hljs bash
# Backup n8n data
tar -czf n8n-backup-$(date +%Y%m%d).tar.gz ./n8n-data/

# PostgreSQL backup
docker exec n8n-postgres pg_dump -U n8n n8n > n8n-db-$(date +%Y%m%d).sql

# Automated backup
cat > /etc/cron.daily/n8n-backup << 'EOF'
#!/bin/bash
docker exec n8n-postgres pg_dump -U n8n n8n | \
  gzip > /backups/n8n-$(date +%Y%m%d).sql.gz
find /backups -name "n8n-*.sql.gz" -mtime +30 -delete
EOF

With n8n on REXE VPS servers, you can automate all your business processes. With 2 GB RAM you can run hundreds of workflows.