# N8N Workflow Automation Server - Setup Plan **Document Created**: 2025-11-29 **Infrastructure Survey Date**: 2025-11-29 14:13:39 **Proxmox Node**: serviceslab **Author**: The Scribe (Steve's Architecture Module) --- ## Executive Summary This document provides a comprehensive plan for deploying n8n (a powerful workflow automation platform) in your Proxmox homelab. After analyzing your current infrastructure, I recommend deploying n8n as an **LXC container** with PostgreSQL database backing, reverse-proxied through your existing Nginx Proxy Manager (NPM) container for SSL termination and secure external access. --- ## I. Current Infrastructure Overview ### Proxmox Host Specifications ``` ╔════════════════════════════════════════════════════════════════╗ ║ PROXMOX NODE: serviceslab ║ ╠════════════════════════════════════════════════════════════════╣ ║ Version: Proxmox VE 8.3.3 ║ ║ Architecture: Single-node cluster ║ ║ CPU: Dual Intel Xeon X5670 @ 2.93GHz ║ ║ (2 sockets × 6 cores × 2 threads = 24 vCPUs) ║ ║ RAM: 173 GB total ║ ║ - Used: 92 GB ║ ║ - Available: 80 GB ║ ║ Management IP: 192.168.2.100/24 ║ ╚════════════════════════════════════════════════════════════════╝ ``` ### Storage Infrastructure ``` ┌─────────────────────────────────────────────────────────────────┐ │ Storage Pool Size Used Free Usage Type │ ├─────────────────────────────────────────────────────────────────┤ │ local 43.9G 6.4G 35G 16% ext4 │ │ Purpose: ISOs, templates, backups │ │ │ │ local-lvm 65.8G N/A N/A N/A LVM │ │ Purpose: VM disk images (thin provisioned) │ │ │ │ Vault (ZFS) 4.36T 124G 4.24T 2% ZFS │ │ Purpose: Primary VM/LXC storage │ │ Health: ONLINE │ │ Compression: Enabled │ │ │ │ PBS-Backups Remote storage at 192.168.2.151 │ │ Purpose: Proxmox Backup Server repository │ │ │ │ iso-share (NFS) 3.1T at 192.168.2.150 │ │ Purpose: Shared ISO library │ └─────────────────────────────────────────────────────────────────┘ ``` ### Network Configuration ``` ┌──────────────────────────────────────────────────────────────────┐ │ Network Topology │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ Physical NIC: eno1 (f0:4d:a2:04:0c:17) │ │ │ │ │ └──► vmbr0 (Primary Bridge) │ │ │ │ │ ├─ IP: 192.168.2.100/24 │ │ ├─ Gateway: 192.168.2.1 │ │ ├─ VLAN-aware: yes │ │ └─ VLAN range: 2-4094 │ │ │ │ vmbr1 (Isolated Network) │ │ └─ IP: 192.168.3.0/24 │ │ Purpose: Internal services network │ │ │ │ VLAN 5: Used for web servers and ansible control │ └──────────────────────────────────────────────────────────────────┘ ``` ### Existing Virtual Machines (QEMU/KVM) ``` ┌────────┬─────────────────────┬───────┬──────────┬──────────┬────────────┐ │ VM ID │ Name │ vCPU │ RAM (GB) │ Disk │ Network │ ├────────┼─────────────────────┼───────┼──────────┼──────────┼────────────┤ │ 100 │ docker-hub │ 4 │ 8.2 │ 100G │ vmbr0 │ │ 101 │ gitlab │ 4 │ 17.0 │ 50G │ vmbr0 │ │ 104 │ ubuntu-dev │ N/A │ N/A │ N/A │ vmbr0 │ │ 105 │ dev │ N/A │ N/A │ N/A │ vmbr0 │ │ 106 │ Ansible-Control │ 2 │ 4.0 │ 32G │ vmbr0.5 │ │ 107 │ ubuntu-docker │ 2 │ 4.0 │ 50G │ vmbr0 │ │ │ (Template) │ │ │ │ │ │ 108 │ CML │ N/A │ N/A │ N/A │ vmbr0 │ │ 109 │ web-server-01 │ 1 │ 2.0 │ 32G │ vmbr0.5 │ │ 110 │ web-server-02 │ N/A │ N/A │ 32G │ vmbr0.5 │ │ 111 │ db-server-01 │ 1 │ 4.0 │ 32G │ vmbr0.5 │ └────────┴─────────────────────┴───────┴──────────┴──────────┴────────────┘ Total VM Resources: ~11 vCPUs, ~40 GB RAM ``` ### Existing LXC Containers ``` ┌────────┬─────────────────────┬───────┬──────────┬──────────┬────────────┐ │ CT ID │ Name │ Cores │ RAM (GB) │ Disk │ IP Address │ ├────────┼─────────────────────┼───────┼──────────┼──────────┼────────────┤ │ 102 │ nginx-proxy-mgr │ 2 │ 4.0 │ 10G │ 192.168. │ │ │ (NPM - Reverse Proxy)│ │ │ │ 2.101/24 │ │ │ │ │ │ │ │ │ 103 │ netbox │ N/A │ N/A │ N/A │ DHCP │ │ │ (IPAM/Docs) │ │ │ │ │ │ │ │ │ │ │ │ │ 112 │ Anytype │ 2 │ 4.0 │ 25G │ DHCP │ │ │ (Knowledge Mgmt) │ │ │ │ │ └────────┴─────────────────────┴───────┴──────────┴──────────┴────────────┘ Features: All containers have nesting=1 (Docker support) ``` ### Resource Availability Assessment ``` ╔═══════════════════════════════════════════════════════════════╗ ║ AVAILABLE RESOURCES ║ ╠═══════════════════════════════════════════════════════════════╣ ║ CPU: ~13 vCPUs available (24 total - ~11 allocated) ║ ║ RAM: ~80 GB available ║ ║ Disk: 4.12 TB available on Vault ZFS pool ║ ║ ║ ║ Verdict: EXCELLENT capacity for n8n deployment ║ ╚═══════════════════════════════════════════════════════════════╝ ``` --- ## II. N8N Architecture Decision: LXC vs VM ### The Verdict: **LXC Container (Recommended)** ``` ┌─────────────────────────────────────────────────────────────────┐ │ LXC vs VM Comparison │ ├──────────────────┬──────────────────────┬───────────────────────┤ │ Factor │ LXC Container │ Virtual Machine │ ├──────────────────┼──────────────────────┼───────────────────────┤ │ Boot Time │ < 5 seconds │ 30-60 seconds │ │ RAM Overhead │ ~100 MB │ ~500 MB │ │ Disk Space │ ~2-5 GB │ ~10-15 GB │ │ Performance │ Near-native │ Slight overhead │ │ Snapshot Speed │ Instant (ZFS) │ 5-30 seconds │ │ Backup Size │ Smaller │ Larger │ │ Docker Support │ Yes (with nesting) │ Native │ │ Resource Isol. │ Good │ Excellent │ │ Complexity │ Simple │ Moderate │ └──────────────────┴──────────────────────┴───────────────────────┘ ``` ### Why LXC for n8n? **Advantages:** 1. **Efficiency**: n8n is a Node.js application with modest requirements. LXC provides near-native performance with minimal overhead. 2. **Fast Deployment**: Container creation takes seconds vs minutes for VMs. 3. **Resource Conservation**: Uses ~500 MB less RAM than a VM, leaving more resources for workflows. 4. **ZFS Snapshots**: Instant snapshots before updates or configuration changes. 5. **Consistency**: Your existing Nginx Proxy Manager (CT 102) is already an LXC container. 6. **Docker Compatibility**: With `nesting=1` feature, the container can run Docker if needed for custom nodes. **Considerations:** - n8n doesn't require kernel-level isolation that VMs provide - No exotic hardware requirements - Perfectly suited for containerized deployment ### When to Use a VM Instead: You would only need a VM if: - Running n8n with Windows-specific integrations - Requiring complete kernel isolation for security - Testing multiple OS environments simultaneously - Planning to run heavy compute workloads within workflows **For typical n8n workflow automation: LXC is the superior choice.** --- ## III. N8N Server Specifications ### Recommended LXC Configuration ``` ┌─────────────────────────────────────────────────────────────────┐ │ N8N LXC Container Specs │ ├─────────────────────────────────────────────────────────────────┤ │ Container ID: 113 (next available) │ │ Hostname: n8n │ │ OS Template: Debian 12 (bookworm) │ │ │ │ vCPU Cores: 2 (scalable to 4 if needed) │ │ RAM: 4096 MB (4 GB) │ │ Swap: 2048 MB (2 GB) │ │ │ │ Root Disk: 20 GB (Vault ZFS pool) │ │ - OS: ~2 GB │ │ - n8n: ~1 GB │ │ - PostgreSQL: ~2 GB │ │ - Workflow data: ~5 GB │ │ - Growth buffer: ~10 GB │ │ │ │ Network: vmbr0 (Primary bridge) │ │ IP Address: 192.168.2.113/24 (static) │ │ Gateway: 192.168.2.1 │ │ DNS: 8.8.8.8, 8.8.4.4 │ │ │ │ Features: nesting=1 (Docker support) │ │ Start on Boot: Yes (onboot: 1) │ │ Unprivileged: Yes (security best practice) │ │ Firewall: Yes (Proxmox firewall enabled) │ └─────────────────────────────────────────────────────────────────┘ ``` ### Performance Scaling Guide ``` Usage Level vCPU RAM Disk Notes ─────────────────────────────────────────────────────────────── Light (< 10 workflows) 2 4 GB 20 GB Recommended start Medium (10-50) 2 6 GB 30 GB Most homelabs Heavy (50-100) 4 8 GB 50 GB Power users Enterprise (100+) 4 12 GB 100 GB Consider VM/K8s ``` --- ## IV. Network Architecture & Integration ### Network Diagram ``` Internet │ ▼ ┌─────────────────┐ │ Router/Firewall │ │ 192.168.2.1 │ └────────┬─────────┘ │ ┌──────────┴───────────┐ │ vmbr0 Bridge │ │ 192.168.2.0/24 │ └──────────┬───────────┘ │ ┌──────────────────┼──────────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ NPM │ │ n8n │ │ GitLab │ │ CT: 102 │ │ CT: 113 │ │ VM: 101 │ │ .101:80 │◄─────┤ .113:5678│ │ DHCP │ │ .101:443 │ └──────────┘ └──────────┘ └──────────┘ │ │ ▼ │ ┌──────────────┐ │ │ PostgreSQL │ │ │ (in CT 113) │ │ │ localhost: │ │ │ 5432 │ │ └──────────────┘ │ ▼ SSL Termination n8n.yourdomain.com ──► https://192.168.2.113:5678 ``` ### IP Address Allocation ``` ┌──────────────────┬────────────────────┬──────────────────────┐ │ IP Address │ Hostname │ Service │ ├──────────────────┼────────────────────┼──────────────────────┤ │ 192.168.2.1 │ router │ Gateway │ │ 192.168.2.100 │ serviceslab │ Proxmox Host │ │ 192.168.2.101 │ Nginx Proxy Manager│ Reverse Proxy │ │ 192.168.2.113 │ n8n │ N8N Server (NEW) │ │ 192.168.2.150 │ NAS │ NFS Storage │ │ 192.168.2.151 │ PBS │ Backup Server │ └──────────────────┴────────────────────┴──────────────────────┘ ``` ### Port Mapping Strategy ``` ┌────────────────────────────────────────────────────────────────┐ │ Port Configuration │ ├────────────────────────────────────────────────────────────────┤ │ Internal n8n Container: │ │ 5678/tcp ──► n8n Web Interface (HTTP) │ │ 5432/tcp ──► PostgreSQL (localhost only) │ │ │ │ Nginx Proxy Manager (CT 102): │ │ 443/tcp ──► HTTPS (proxies to n8n:5678) │ │ 80/tcp ──► HTTP (redirects to HTTPS) │ │ 81/tcp ──► NPM Admin UI (LAN only) │ │ │ │ External Access: │ │ https://n8n.yourdomain.com ──► NPM:443 ──► n8n:5678 │ └────────────────────────────────────────────────────────────────┘ ``` ### Firewall Rules ``` # Proxmox Firewall Configuration for CT 113 (n8n) Direction Protocol Source Dest Port Action Comment ───────────────────────────────────────────────────────────────── IN TCP 192.168.2.101 5678 ACCEPT NPM proxy IN TCP 192.168.2.0/24 22 ACCEPT SSH admin IN TCP 0.0.0.0/0 5678 DROP Block direct OUT TCP any 80,443 ACCEPT Updates/webhooks OUT TCP any 25,587 ACCEPT Email (SMTP) OUT UDP any 53 ACCEPT DNS ``` --- ## V. Database Architecture ### PostgreSQL vs SQLite Decision Matrix ``` ┌───────────────────┬─────────────────────┬─────────────────────┐ │ Factor │ PostgreSQL (Rec.) │ SQLite │ ├───────────────────┼─────────────────────┼─────────────────────┤ │ Performance │ Excellent │ Good │ │ Concurrent Access │ Full support │ Limited │ │ Data Integrity │ ACID compliant │ ACID compliant │ │ Backup/Restore │ Standard tools │ File copy │ │ Scaling │ Unlimited │ Limited to 1-2 GB │ │ HA/Clustering │ Yes │ No │ │ Setup Complexity │ Moderate │ Zero config │ │ Production Ready │ Yes │ For small setups │ └───────────────────┴─────────────────────┴─────────────────────┘ ``` **Recommendation**: **PostgreSQL** for production reliability and future growth. ### PostgreSQL Configuration ``` ┌─────────────────────────────────────────────────────────────────┐ │ PostgreSQL Database Specifications │ ├─────────────────────────────────────────────────────────────────┤ │ Version: PostgreSQL 16 (latest stable) │ │ Installation: Same container as n8n (CT 113) │ │ Database Name: n8n_db │ │ Database User: n8n_user │ │ Listen Address: 127.0.0.1 (localhost only) │ │ Port: 5432 │ │ Max Connections: 20 │ │ Shared Buffers: 512 MB │ │ Maintenance Work: 128 MB │ │ WAL Level: replica (for backups) │ │ │ │ Backup Strategy: │ │ - Daily pg_dump to /var/backups/n8n/ │ │ - Proxmox container backup to PBS (weekly) │ │ - ZFS snapshots before updates (manual) │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## VI. Integration with Existing Services ### A. Nginx Proxy Manager (CT 102) Your existing Nginx Proxy Manager container will handle: 1. **SSL/TLS Termination** - Let's Encrypt certificates (via NPM UI) 2. **HTTPS Enforcement** - HTTP to HTTPS redirect 3. **Security Headers** - HSTS, CSP, X-Frame-Options 4. **Rate Limiting** - Prevent abuse 5. **Access Logging** - Centralized logging 6. **Web-based Management** - No manual config file editing required **Nginx Proxy Manager Overview:** Nginx Proxy Manager (NPM) is a Docker-based reverse proxy management tool that provides: - **Web UI**: Accessible at `http://192.168.2.101:81` - **Let's Encrypt Integration**: One-click SSL certificate generation and renewal - **GUI Configuration**: Point-and-click proxy host creation - **Built-in Access Control**: IP whitelisting and basic authentication - **Real-time Monitoring**: View proxy status and logs through dashboard **GitHub**: https://github.com/NginxProxyManager/nginx-proxy-manager **Configuration for n8n (via NPM Web UI):** Instead of manually editing nginx configuration files, you'll configure the n8n proxy through NPM's web interface in Phase 7. Basic setup: 1. **Access NPM Admin UI**: `http://192.168.2.101:81` 2. **Create Proxy Host** with these settings: - Domain: `n8n.yourdomain.com` - Forward to: `192.168.2.113:5678` - Enable WebSockets support 3. **Configure SSL**: Request Let's Encrypt certificate via UI 4. **Advanced Settings** (optional custom nginx config): ```nginx # Custom Nginx directives for n8n (added via NPM Advanced tab) client_max_body_size 50M; # Extended timeouts for long-running workflows proxy_connect_timeout 300; proxy_send_timeout 300; proxy_read_timeout 300; send_timeout 300; # Additional security headers add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; # WebSocket keep-alive proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; ``` **NPM Architecture:** - **Admin UI**: Port 81 (LAN access only) - **Proxy Traffic**: Ports 80/443 - **Docker-based**: Runs in containers on CT 102 - **Auto-renewal**: Let's Encrypt certificates renew automatically ### B. GitLab Integration (VM 101) N8N can automate GitLab workflows: - **CI/CD Triggers** - Start pipelines on external events - **Issue Management** - Auto-create/update issues from tickets - **Merge Request Automation** - Code review workflows - **Repository Monitoring** - Track commits, tags, releases - **Deployment Notifications** - Slack/email on deployments **Connection:** - GitLab API available at VM 101's IP (configure in n8n) - Create Personal Access Token in GitLab with API scope - Store token in n8n credentials manager ### C. Docker Hub (VM 100) Integration possibilities: - **Image Update Notifications** - Alert on new image versions - **Automated Pulls** - Trigger container updates - **Build Monitoring** - Track image builds ### D. Ansible Control (VM 106) N8N can trigger Ansible playbooks: - **Infrastructure Automation** - Run playbooks via webhook - **Configuration Management** - Scheduled config updates - **Orchestration** - Multi-system deployment workflows **Setup Method:** - n8n HTTP Request node → Ansible AWX/Tower REST API - Or: n8n Execute Command node via SSH to Ansible Control VM --- ## VII. Step-by-Step Setup Plan ### Phase 1: Container Creation (15 minutes) ``` ┌─────────────────────────────────────────────────────────────────┐ │ Create LXC Container │ └─────────────────────────────────────────────────────────────────┘ Option A: Proxmox Web UI Method ───────────────────────────────── 1. Login to Proxmox at https://192.168.2.100:8006 2. Click "Create CT" button 3. Configure container: General: ├─ Node: serviceslab ├─ CT ID: 113 ├─ Hostname: n8n ├─ Unprivileged: ☑ (checked) └─ Password: [Set strong password] Template: └─ Storage: local Template: debian-12-standard (or ubuntu-24.04-standard) Disks: ├─ Storage: Vault └─ Disk size: 20 GB CPU: └─ Cores: 2 Memory: ├─ Memory: 4096 MB └─ Swap: 2048 MB Network: ├─ Bridge: vmbr0 ├─ IPv4: Static ├─ IPv4/CIDR: 192.168.2.113/24 ├─ Gateway: 192.168.2.1 └─ IPv6: SLAAC (or disable) DNS: ├─ DNS domain: apophisnetworking.net └─ DNS servers: 8.8.8.8 8.8.4.4 4. Click "Confirm" then "Finish" 5. Enable features: ├─ Right-click CT 113 → Options ├─ Features → Edit └─ Check: "nesting" ☑ 6. Start container Option B: CLI Method (Proxmox SSH) ────────────────────────────────── # SSH to Proxmox host ssh root@192.168.2.100 # Download template (if not present) pveam update pveam download local debian-12-standard_12.7-1_amd64.tar.zst # Create container pct create 113 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \ --hostname n8n \ --cores 2 \ --memory 4096 \ --swap 2048 \ --rootfs Vault:20 \ --net0 name=eth0,bridge=vmbr0,ip=192.168.2.113/24,gw=192.168.2.1 \ --nameserver 8.8.8.8 \ --features nesting=1 \ --unprivileged 1 \ --onboot 1 # Start container pct start 113 # Enter container pct enter 113 ``` ### Phase 2: System Preparation (10 minutes) ```bash # Inside CT 113 (after pct enter 113) # Update system apt update && apt upgrade -y # Install basic prerequisites apt install -y \ curl \ wget \ git \ gnupg2 \ ca-certificates \ lsb-release \ ufw # Add PostgreSQL Official Repository # Note: PostgreSQL 16 is not in standard Debian repositories echo "Setting up PostgreSQL 16 from official repository..." # Add PostgreSQL GPG key curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \ gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg # Add PostgreSQL APT repository sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' # Update package list with new repository apt update # Install PostgreSQL 16 apt install -y postgresql-16 postgresql-contrib-16 # Verify installation psql --version # Configure timezone timedatectl set-timezone America/New_York # Adjust to your TZ ``` ### Phase 3: PostgreSQL Setup (10 minutes) > **⚠️ POSTGRESQL 15+ COMPATIBILITY NOTICE** > > PostgreSQL 15 and later versions introduced a **breaking change** that removed the default `CREATE` privilege on the `public` schema. This affects n8n's ability to create tables during initial database migration. > > This guide includes the necessary permission grants for PostgreSQL 15+. If you're using PostgreSQL 14 or earlier, these steps are still safe to execute but not strictly required. > > **Affected Versions**: PostgreSQL 15, 16, 17+ > **Reference**: [PostgreSQL 15 Release Notes - Public Schema Permissions](https://www.postgresql.org/docs/15/ddl-schemas.html#DDL-SCHEMAS-PUBLIC) ```bash # Switch to postgres user and create database with proper locale and permissions sudo -u postgres psql << 'EOSQL' -- Create database user CREATE USER n8n_user WITH ENCRYPTED PASSWORD 'YourSecurePassword123!'; -- Create database with C.utf8 locale (Debian 12 minimal LXC compatibility) CREATE DATABASE n8n_db OWNER n8n_user ENCODING 'UTF8' LC_COLLATE = 'C.utf8' LC_CTYPE = 'C.utf8' TEMPLATE template0; -- Connect to the database to grant schema permissions \c n8n_db -- PostgreSQL 15+ REQUIRED: Grant CREATE on public schema GRANT ALL ON SCHEMA public TO n8n_user; -- Grant privileges on all current and future objects GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO n8n_user; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO n8n_user; GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO n8n_user; -- Ensure future objects are also granted to n8n_user ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO n8n_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO n8n_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO n8n_user; -- Verify database settings SELECT datname, datcollate, datctype, pg_get_userbyid(datdba) as owner FROM pg_database WHERE datname = 'n8n_db'; \q EOSQL ``` > **📝 LOCALE SELECTION RATIONALE** > > This guide uses `C.utf8` locale for maximum compatibility with minimal LXC containers: > > - **Debian 12 Minimal LXC**: Only includes `C`, `C.utf8`, and `POSIX` locales by default > - **Case Sensitivity**: PostgreSQL locale names are case-sensitive (`C.utf8` ≠ `C.UTF-8`) > - **Verification**: Run `locale -a` to see available locales on your system > > If you need full locale support (e.g., `en_US.UTF-8`), install the `locales` package: > ```bash > apt install locales > dpkg-reconfigure locales > ``` > > Then recreate the database with your preferred locale. For most automation workflows, `C.utf8` is sufficient and provides better performance for ASCII-based data. ```bash # Configure PostgreSQL cat >> /etc/postgresql/16/main/postgresql.conf << 'EOF' # N8N Optimizations max_connections = 20 shared_buffers = 512MB effective_cache_size = 2GB maintenance_work_mem = 128MB checkpoint_completion_target = 0.9 wal_buffers = 16MB default_statistics_target = 100 random_page_cost = 1.1 effective_io_concurrency = 200 work_mem = 26214kB min_wal_size = 1GB max_wal_size = 4GB EOF # Restart PostgreSQL systemctl restart postgresql systemctl enable postgresql # Test connection and verify permissions PGPASSWORD='YourSecurePassword123!' psql -U n8n_user -d n8n_db -h localhost -c "SELECT version();" # Verify n8n_user can create tables (critical for PostgreSQL 15+) echo "Testing n8n_user permissions..." PGPASSWORD='YourSecurePassword123!' psql -U n8n_user -d n8n_db -h localhost << 'TEST_SQL' -- This is what n8n will attempt during first startup CREATE TABLE permission_test ( id SERIAL PRIMARY KEY, test_data VARCHAR(100) ); DROP TABLE permission_test; SELECT 'PostgreSQL permissions OK' AS status; TEST_SQL if [ $? -eq 0 ]; then echo "✓ SUCCESS: n8n_user has correct permissions" else echo "✗ ERROR: Permission test failed - check PostgreSQL 15+ grants" exit 1 fi ``` ### Phase 4: Node.js & n8n Installation (15 minutes) ```bash # Install Node.js 20.x (LTS - recommended for n8n) curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt install -y nodejs # Verify Node.js installation node --version # Should show v20.x.x npm --version # Should show 10.x.x # Install n8n globally npm install -g n8n # Create n8n system user useradd -r -m -d /opt/n8n -s /bin/bash n8n # Create directory structure mkdir -p /opt/n8n/{.n8n,backups,logs} chown -R n8n:n8n /opt/n8n ``` ### Phase 5: N8N Configuration (10 minutes) > **⚠️ CRITICAL: N8N_ENCRYPTION_KEY GENERATION** > > The `N8N_ENCRYPTION_KEY` is used to encrypt credentials stored in the database. This key: > - **MUST** be a 64-character hexadecimal string (32 bytes) > - **CANNOT** be changed after initial setup (encrypted data becomes unreadable) > - **MUST** be generated before creating the .env file > > **Common Mistake**: Writing `N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)` in the .env file results in the **literal string** being stored, not the generated key. This causes n8n to crash immediately on startup. > > **Correct Approach**: Generate the key first, then insert it as a static value. ```bash # STEP 1: Generate encryption key FIRST (execute in subshell to capture output) ENCRYPTION_KEY=$(openssl rand -hex 32) # STEP 2: Verify key was generated (should be 64 characters) echo "Generated Encryption Key: $ENCRYPTION_KEY" echo "Key Length: ${#ENCRYPTION_KEY} characters (should be 64)" # STEP 3: Create .env file with the generated key (note: EOF without quotes to allow variable expansion) cat > /opt/n8n/.env << EOF # Database Configuration DB_TYPE=postgresdb DB_POSTGRESDB_HOST=localhost DB_POSTGRESDB_PORT=5432 DB_POSTGRESDB_DATABASE=n8n_db DB_POSTGRESDB_USER=n8n_user DB_POSTGRESDB_PASSWORD=YourSecurePassword123! # n8n Configuration N8N_PROTOCOL=https N8N_HOST=n8n.yourdomain.com N8N_PORT=5678 N8N_PATH=/ WEBHOOK_URL=https://n8n.yourdomain.com/ # Execution EXECUTIONS_DATA_SAVE_ON_ERROR=all EXECUTIONS_DATA_SAVE_ON_SUCCESS=all EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true # Performance N8N_PAYLOAD_SIZE_MAX=16 EXECUTIONS_TIMEOUT=300 EXECUTIONS_TIMEOUT_MAX=3600 # Timezone GENERIC_TIMEZONE=America/New_York # Security - DO NOT MODIFY AFTER INITIAL SETUP N8N_BASIC_AUTH_ACTIVE=false N8N_JWT_AUTH_ACTIVE=true N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY} # Paths N8N_USER_FOLDER=/opt/n8n/.n8n N8N_LOG_LOCATION=/opt/n8n/logs/ N8N_LOG_LEVEL=info EOF # STEP 4: Verify the .env file contains actual key, not command substitution echo "" echo "Verifying .env file encryption key..." grep "N8N_ENCRYPTION_KEY" /opt/n8n/.env echo "" # Validation check if grep -q "N8N_ENCRYPTION_KEY=\$(openssl" /opt/n8n/.env; then echo "✗ ERROR: Encryption key was not expanded! Contains literal command." echo "Fix required before proceeding." exit 1 elif grep -q "^N8N_ENCRYPTION_KEY=[a-f0-9]\{64\}$" /opt/n8n/.env; then echo "✓ SUCCESS: N8N_ENCRYPTION_KEY properly configured (64 hex characters)" else echo "⚠ WARNING: Encryption key format unexpected. Manual verification required." fi # Secure environment file chown n8n:n8n /opt/n8n/.env chmod 600 /opt/n8n/.env ``` > **🔍 VERIFICATION: Encryption Key Format** > > Before proceeding, manually inspect `/opt/n8n/.env` and verify: > > **CORRECT** ✅: > ``` > N8N_ENCRYPTION_KEY=a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456 > ``` > > **INCORRECT** ❌: > ``` > N8N_ENCRYPTION_KEY=$(openssl rand -hex 32) > N8N_ENCRYPTION_KEY= > ``` > > If the key is missing or contains a command string: > ```bash > # Regenerate and update the .env file > NEW_KEY=$(openssl rand -hex 32) > sed -i "s/^N8N_ENCRYPTION_KEY=.*/N8N_ENCRYPTION_KEY=${NEW_KEY}/" /opt/n8n/.env > ``` ### Phase 6: Systemd Service Creation (5 minutes) ```bash # Create systemd service cat > /etc/systemd/system/n8n.service << 'EOF' [Unit] Description=n8n - Workflow Automation Documentation=https://docs.n8n.io After=network.target postgresql.service Wants=postgresql.service [Service] Type=simple User=n8n Group=n8n WorkingDirectory=/opt/n8n EnvironmentFile=/opt/n8n/.env ExecStart=/usr/bin/n8n start # Restart configuration Restart=always RestartSec=10 StartLimitInterval=60 StartLimitBurst=5 # Security NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ReadWritePaths=/opt/n8n ProtectHome=true # Logging StandardOutput=append:/opt/n8n/logs/n8n.log StandardError=append:/opt/n8n/logs/n8n-error.log [Install] WantedBy=multi-user.target EOF # Reload systemd and start n8n systemctl daemon-reload systemctl enable n8n systemctl start n8n # Check status systemctl status n8n # View logs journalctl -u n8n -f ``` ### Phase 7: Nginx Proxy Manager Configuration (10 minutes) Unlike traditional nginx configuration, NPM uses a web-based GUI for all proxy management. No SSH required. > **🔒 SSL TERMINATION ARCHITECTURE** > > Understanding the request flow is critical for correct proxy configuration: > > ``` > Client Browser ──HTTPS(443)──► NPM ──HTTP(5678)──► n8n Container > [Encrypted] [Unencrypted] > [Internal Network] > ``` > > **Key Concepts**: > 1. **SSL Termination**: NPM handles SSL/TLS encryption/decryption > 2. **Backend Protocol**: NPM communicates with n8n over **HTTP** (not HTTPS) > 3. **Internal Security**: Traffic between NPM and n8n is on your private LAN (192.168.2.x) > > **Common Misconfiguration**: Setting scheme to `https` when n8n listens on HTTP causes **502 Bad Gateway** errors because NPM attempts SSL handshake with a non-SSL backend. > > This is the standard reverse proxy pattern and is **secure** because: > - Client-to-NPM traffic is encrypted (HTTPS) > - NPM-to-backend traffic is on your isolated internal network > - No external parties can intercept the internal HTTP traffic **Prerequisites:** - NPM is installed and running on CT 102 - NPM admin UI accessible at `http://192.168.2.101:81` - DNS A record for `n8n.yourdomain.com` pointing to your public IP #### Step 1: Access NPM Admin Interface From your workstation browser: - Navigate to: `http://192.168.2.101:81` - **First-time login credentials:** - Email: `admin@example.com` - Password: `changeme` - **IMPORTANT:** You will be prompted to change these immediately #### Step 2: Create Proxy Host for n8n 1. **Navigate to Proxy Hosts**: - Click "Hosts" → "Proxy Hosts" in the NPM dashboard - Click "Add Proxy Host" button 2. **Configure Details Tab**: ``` Domain Names: n8n.yourdomain.com Scheme: http ⚠️ CRITICAL: Use 'http' not 'https' (n8n listens on HTTP, NPM handles SSL) Forward Hostname/IP: 192.168.2.113 (n8n container IP) Forward Port: 5678 (n8n default port) Options: ☑ Cache Assets ☑ Block Common Exploits ☑ Websockets Support (CRITICAL for n8n!) ☐ Access List (optional - configure if needed) ``` > **⚠️ IMPORTANT: Understanding the "Scheme" Setting** > > The "Scheme" dropdown controls how NPM communicates with the BACKEND service, NOT how external clients connect to NPM. > > **Correct Configuration:** > - Scheme: `http` ← Backend communication (NPM → n8n at 192.168.2.113:5678) > - Force SSL: `☑ Enabled` ← External connections (browser → NPM) > > **Traffic Flow:** > 1. External client connects via HTTPS to NPM (SSL termination at proxy) > 2. NPM decrypts HTTPS traffic and validates certificates > 3. NPM forwards plain HTTP to backend at 192.168.2.113:5678 > 4. n8n receives HTTP request (no SSL certificate or processing needed) > 5. n8n sends HTTP response back to NPM > 6. NPM encrypts response with Let's Encrypt certificate > 7. NPM sends HTTPS response to external client > > **Why This Matters:** > - n8n listens on HTTP port 5678 with no SSL certificate configured > - Using `https` scheme causes NPM to attempt TLS connection to backend > - Backend cannot complete TLS handshake → 502 Bad Gateway error > - This is standard reverse proxy SSL termination architecture > > **Common Mistake:** > ❌ Setting Scheme to `https` thinking it affects external connections > ✅ External HTTPS is controlled by "Force SSL" in SSL tab (next step) 3. **Configure SSL Tab**: ``` SSL Certificate: Request a new SSL Certificate ☑ Force SSL ☑ HTTP/2 Support ☑ HSTS Enabled ☐ HSTS Subdomains (not needed for n8n) Email Address: your-email@domain.com ☑ I Agree to the Let's Encrypt Terms of Service ``` 4. **Configure Advanced Tab (Optional)**: ```nginx # Custom Nginx Configuration # Paste the following for optimal n8n performance: client_max_body_size 50M; # Extended timeouts for long-running workflows proxy_connect_timeout 300; proxy_send_timeout 300; proxy_read_timeout 300; send_timeout 300; # Additional security headers add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; # WebSocket keep-alive proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; ``` 5. **Save Configuration**: - Click "Save" button - NPM will automatically: - Generate nginx configuration - Request Let's Encrypt certificate - Configure SSL settings - Reload nginx - Enable automatic certificate renewal (every 60 days) #### Step 3: Verify Configuration ```bash # Test n8n accessibility through NPM curl -I https://n8n.yourdomain.com # Expected response: HTTP/2 200 server: nginx content-type: text/html; charset=utf-8 strict-transport-security: max-age=31536000 x-frame-options: SAMEORIGIN ... ``` #### Step 4: Verify DNS and Port Forwarding **DNS Configuration:** Ensure your domain's DNS has an A record pointing to your public IP: ``` Type: A Host: n8n Points to: TTL: 3600 ``` **Router Port Forwarding** (if behind NAT): ``` External Port 80 → 192.168.2.101:80 External Port 443 → 192.168.2.101:443 ``` #### NPM Monitoring & Management **View Logs**: - Click on proxy host → "Actions" → "View Logs" - Real-time request logging and error tracking **Certificate Renewal**: - Automatic renewal via NPM (every 60 days) - Manual renewal: Edit proxy host → SSL tab → "Renew Certificate" **Disable/Enable Proxy**: - Toggle switch next to proxy host name - No need to restart services #### Troubleshooting NPM **Issue: NPM Web UI not accessible** ```bash # Check NPM container status on CT 102 pct enter 102 docker ps | grep nginx-proxy-manager docker logs nginx-proxy-manager # Restart NPM if needed docker restart nginx-proxy-manager ``` **Issue: SSL certificate generation fails** - Verify DNS propagation: `nslookup n8n.yourdomain.com` - Check port 80/443 accessibility from internet - Review Let's Encrypt rate limits (5 certs/week per domain) - Check NPM logs for specific error messages **Issue: n8n not accessible through NPM** - Verify n8n is running: `curl http://192.168.2.113:5678` - Check NPM proxy host configuration (correct IP/port) - Verify firewall allows 192.168.2.101 → 192.168.2.113:5678 - Review NPM access logs for 502/504 errors ### Phase 8: Firewall Configuration (5 minutes) ```bash # On n8n container (CT 113) ufw default deny incoming ufw default allow outgoing ufw allow from 192.168.2.0/24 to any port 22 comment 'SSH from LAN' ufw allow from 192.168.2.101 to any port 5678 comment 'NPM reverse proxy' ufw enable # On Proxmox host (configure Proxmox firewall) # Via Web UI: # Datacenter → Firewall → Add rules for CT 113 # Or via CLI: cat >> /etc/pve/firewall/113.fw << 'EOF' [OPTIONS] enable: 1 [RULES] IN ACCEPT -source 192.168.2.101 -dport 5678 -proto tcp -log nolog IN ACCEPT -source 192.168.2.0/24 -dport 22 -proto tcp -log nolog IN DROP -dport 5678 -proto tcp -log warning EOF # Apply firewall pve-firewall compile ``` ### Phase 9: Initial n8n Setup (10 minutes) ``` ┌─────────────────────────────────────────────────────────────────┐ │ First-Time Access │ └─────────────────────────────────────────────────────────────────┘ 1. Open browser to: https://n8n.yourdomain.com 2. Create owner account: ├─ Email: your@email.com ├─ First Name: [Your Name] ├─ Last Name: [Your Last Name] └─ Password: [Strong password] 3. Setup tour will guide you through: ├─ Creating your first workflow ├─ Adding credentials └─ Testing a simple automation 4. Configure critical settings: Settings → General: ├─ Timezone: Match your location ├─ Execution timeout: 300 seconds (default) └─ Save data: All executions (for debugging) 5. Setup credentials for integrations: Credentials → Add Credential: ├─ GitLab API (for VM 101) ├─ SMTP (for email notifications) ├─ Webhook authentication └─ Any other services you use ``` ### Phase 10: Backup Configuration (15 minutes) ```bash # On n8n container (CT 113) # Create backup script cat > /opt/n8n/backup.sh << 'EOF' #!/bin/bash # N8N Backup Script BACKUP_DIR="/opt/n8n/backups" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_NAME="n8n_backup_${DATE}" # Create backup directory mkdir -p ${BACKUP_DIR}/${BACKUP_NAME} # Backup PostgreSQL database PGPASSWORD='YourSecurePassword123!' pg_dump \ -U n8n_user \ -h localhost \ -d n8n_db \ -F c \ -f ${BACKUP_DIR}/${BACKUP_NAME}/n8n_db.dump # Backup n8n user folder tar -czf ${BACKUP_DIR}/${BACKUP_NAME}/n8n_data.tar.gz \ -C /opt/n8n/.n8n . # Backup environment file cp /opt/n8n/.env ${BACKUP_DIR}/${BACKUP_NAME}/ # Create backup manifest cat > ${BACKUP_DIR}/${BACKUP_NAME}/MANIFEST.txt << MANIFEST N8N Backup Date: $(date) Database: n8n_db.dump User Data: n8n_data.tar.gz Environment: .env MANIFEST # Compress backup cd ${BACKUP_DIR} tar -czf ${BACKUP_NAME}.tar.gz ${BACKUP_NAME} rm -rf ${BACKUP_NAME} # Keep last 7 daily backups find ${BACKUP_DIR} -name "n8n_backup_*.tar.gz" -mtime +7 -delete echo "Backup completed: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" EOF chmod +x /opt/n8n/backup.sh # Create daily backup cron job cat > /etc/cron.d/n8n-backup << 'EOF' # Daily n8n backup at 2 AM 0 2 * * * n8n /opt/n8n/backup.sh >> /opt/n8n/logs/backup.log 2>&1 EOF # Test backup sudo -u n8n /opt/n8n/backup.sh # Configure Proxmox container backup # On Proxmox host: # Datacenter → Backup → Add # Schedule: Weekly, Sunday 1:00 AM # Storage: PBS-Backups # Mode: Snapshot # Compression: ZSTD # Include: CT 113 ``` --- ## VIII. Post-Installation Checklist ``` ┌─────────────────────────────────────────────────────────────────┐ │ Verification Checklist │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ □ Container CT 113 created and running │ │ □ Static IP 192.168.2.113 configured │ │ □ PostgreSQL installed and accessible │ │ □ n8n service running (systemctl status n8n) │ │ □ n8n accessible at https://n8n.yourdomain.com │ │ □ SSL certificate valid and auto-renewing │ │ □ Nginx reverse proxy forwarding correctly │ │ □ Firewall rules in place │ │ □ Owner account created in n8n │ │ □ Daily database backups configured │ │ □ Proxmox container backups scheduled │ │ □ Can create and execute test workflow │ │ □ Webhook URL working (test with curl) │ │ □ Email notifications configured (optional) │ │ □ GitLab integration tested (optional) │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## IX. Testing & Validation ### Test 1: Basic Connectivity ```bash # From any machine on 192.168.2.0/24 network curl -I http://192.168.2.113:5678 # Expected: HTTP/1.1 200 OK # HTTPS through NPM curl -I https://n8n.yourdomain.com # Expected: HTTP/2 200 (or 301 → 200) ``` ### Test 2: Create Sample Workflow ``` 1. Login to n8n web interface 2. Click "+ Add workflow" 3. Add nodes: ├─ Schedule Trigger (every 1 hour) ├─ HTTP Request (GET https://api.github.com/zen) └─ Send Email (to your address) 4. Configure email credentials 5. Execute workflow manually 6. Check email received 7. Enable workflow 8. Monitor executions tab ``` ### Test 3: GitLab Integration ``` 1. In GitLab (VM 101): └─ Settings → Access Tokens Create token with 'api' scope 2. In n8n: └─ Credentials → Add → GitLab API Enter: GitLab URL and Access Token 3. Create workflow: ├─ Webhook Trigger (on /webhook/gitlab) ├─ GitLab node (Get issue details) └─ Log to file 4. Configure GitLab webhook: └─ Project → Settings → Webhooks URL: https://n8n.yourdomain.com/webhook/gitlab Events: Issues 5. Test: Create issue in GitLab 6. Verify: Check n8n execution log ``` ### Test 4: Database Performance ```bash # On CT 113 sudo -u postgres psql -d n8n_db -c " SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC LIMIT 10; " ``` ### Test 5: Backup & Restore ```bash # Test backup sudo -u n8n /opt/n8n/backup.sh # Simulate disaster: Stop n8n, drop database systemctl stop n8n sudo -u postgres psql -c "DROP DATABASE n8n_db;" # Restore from backup sudo -u postgres psql -c "CREATE DATABASE n8n_db OWNER n8n_user;" LATEST_BACKUP=$(ls -t /opt/n8n/backups/*.tar.gz | head -1) tar -xzf $LATEST_BACKUP -C /tmp/ PGPASSWORD='YourSecurePassword123!' pg_restore \ -U n8n_user \ -d n8n_db \ /tmp/*/n8n_db.dump # Restart n8n systemctl start n8n # Verify workflows still exist ``` --- ## X. Monitoring & Maintenance ### Health Monitoring ```bash # Create monitoring script cat > /usr/local/bin/n8n-health-check << 'EOF' #!/bin/bash # N8N Health Check Script # Check if n8n service is running if ! systemctl is-active --quiet n8n; then echo "CRITICAL: n8n service is not running" exit 2 fi # Check if n8n responds to HTTP HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5678/healthz) if [ "$HTTP_CODE" != "200" ]; then echo "WARNING: n8n healthcheck returned $HTTP_CODE" exit 1 fi # Check PostgreSQL if ! PGPASSWORD='YourSecurePassword123!' psql -U n8n_user -d n8n_db -h localhost -c "SELECT 1;" > /dev/null 2>&1; then echo "CRITICAL: PostgreSQL connection failed" exit 2 fi # Check disk space DISK_USAGE=$(df -h /opt/n8n | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$DISK_USAGE" -gt 80 ]; then echo "WARNING: Disk usage at ${DISK_USAGE}%" exit 1 fi echo "OK: All checks passed" exit 0 EOF chmod +x /usr/local/bin/n8n-health-check # Schedule health check every 5 minutes cat > /etc/cron.d/n8n-health << 'EOF' */5 * * * * root /usr/local/bin/n8n-health-check >> /var/log/n8n-health.log 2>&1 EOF ``` ### Log Rotation ```bash # Configure logrotate cat > /etc/logrotate.d/n8n << 'EOF' /opt/n8n/logs/*.log { daily rotate 14 compress delaycompress missingok notifempty create 0640 n8n n8n sharedscripts postrotate systemctl reload n8n > /dev/null 2>&1 || true endscript } EOF ``` ### Update Procedure ```bash # Create update script cat > /opt/n8n/update-n8n.sh << 'EOF' #!/bin/bash # N8N Update Script set -e echo "Starting n8n update process..." # Backup before update echo "Creating backup..." /opt/n8n/backup.sh # Stop n8n service echo "Stopping n8n..." systemctl stop n8n # Update n8n echo "Updating n8n..." npm update -g n8n # Clear npm cache npm cache clean --force # Start n8n service echo "Starting n8n..." systemctl start n8n # Wait for service to be ready sleep 10 # Check health if /usr/local/bin/n8n-health-check; then echo "Update completed successfully!" n8n --version else echo "ERROR: Health check failed after update!" echo "Restoring from backup may be necessary" exit 1 fi EOF chmod +x /opt/n8n/update-n8n.sh ``` ### Maintenance Schedule ``` ┌────────────────────────────────────────────────────────────────┐ │ Maintenance Calendar │ ├────────────────────────────────────────────────────────────────┤ │ │ │ Daily: │ │ └─ 02:00 AM - Database backup │ │ │ │ Weekly: │ │ └─ Sunday 01:00 AM - Proxmox container backup │ │ │ │ Monthly: │ │ ├─ Review execution logs │ │ ├─ Check disk space usage │ │ ├─ Update n8n to latest version │ │ ├─ Review and optimize workflows │ │ └─ Test backup restoration │ │ │ │ Quarterly: │ │ ├─ Review security headers │ │ ├─ Update SSL certificates (automatic via Let's Encrypt) │ │ ├─ Audit user access │ │ └─ PostgreSQL VACUUM ANALYZE │ │ │ └────────────────────────────────────────────────────────────────┘ ``` --- ## XI. Troubleshooting Guide ### Common Issues & Solutions #### Issue 1: n8n Won't Start ```bash # Check service status systemctl status n8n # Check logs journalctl -u n8n -n 50 --no-pager # Common causes: # 1. PostgreSQL not running systemctl status postgresql systemctl start postgresql # 2. Database connection error PGPASSWORD='YourSecurePassword123!' psql -U n8n_user -d n8n_db -h localhost # 3. Port already in use ss -tulpn | grep 5678 # 4. Permission issues chown -R n8n:n8n /opt/n8n ``` #### Issue 2: Can't Access via HTTPS ```bash # Check NPM status (on CT 102) pct enter 102 docker ps | grep nginx-proxy-manager docker logs nginx-proxy-manager # View NPM proxy host configuration # Access http://192.168.2.101:81 and check proxy host settings # Check SSL certificate # NPM Admin UI → SSL Certificates tab shows all certs and expiry dates # Renew if needed (NPM auto-renews, but can manually trigger) # NPM UI → Proxy Host → Edit → SSL → Renew Certificate button # Check firewall ufw status # Ensure 443 is open on NPM container (CT 102) # Test backend connectivity curl http://192.168.2.113:5678 ``` #### Issue 3: Workflows Timeout ```bash # Increase timeout in /opt/n8n/.env EXECUTIONS_TIMEOUT=600 EXECUTIONS_TIMEOUT_MAX=7200 # Restart n8n systemctl restart n8n # Also check NPM timeout in proxy host Advanced settings # proxy_read_timeout 600; ``` #### Issue 4: Database Connection Pool Exhausted ```bash # Edit /etc/postgresql/16/main/postgresql.conf max_connections = 50 # Restart PostgreSQL systemctl restart postgresql # Monitor connections sudo -u postgres psql -c " SELECT count(*) FROM pg_stat_activity WHERE datname = 'n8n_db'; " ``` #### Issue 5: High Memory Usage ```bash # Check memory usage free -h top -o %MEM # Reduce n8n memory if needed in /opt/n8n/.env N8N_PAYLOAD_SIZE_MAX=8 # Restart n8n systemctl restart n8n # Consider increasing container RAM # On Proxmox: pct set 113 -memory 6144 ``` --- ## XII. Security Hardening ### Security Checklist ``` ┌─────────────────────────────────────────────────────────────────┐ │ Security Hardening Checklist │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ □ Use strong, unique passwords (20+ characters) │ │ □ Enable HTTPS only (HTTP → HTTPS redirect) │ │ □ Configure HSTS header (max-age=31536000) │ │ □ Implement rate limiting in NPM (if available) │ │ □ Use unprivileged LXC container │ │ □ Firewall blocks direct access to port 5678 │ │ □ PostgreSQL listens on localhost only │ │ □ Regular security updates (apt update && upgrade) │ │ □ Backup encryption for sensitive workflows │ │ □ Multi-factor authentication (n8n Cloud feature) │ │ □ IP whitelist for admin access (optional) │ │ □ Credential encryption enabled (N8N_ENCRYPTION_KEY) │ │ □ Audit logs enabled and monitored │ │ □ Fail2ban for SSH brute-force protection │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### Additional Security Measures ```bash # Install fail2ban (on CT 113) apt install -y fail2ban # Configure fail2ban for SSH cat > /etc/fail2ban/jail.local << 'EOF' [sshd] enabled = true port = ssh logpath = /var/log/auth.log maxretry = 3 bantime = 3600 EOF systemctl enable fail2ban systemctl start fail2ban # Implement NPM rate limiting # Navigate to NPM Admin UI → Proxy Host → n8n → Advanced tab # Add custom configuration: limit_req_zone $binary_remote_addr zone=n8n_limit:10m rate=10r/s; # In location block (Advanced config): limit_req zone=n8n_limit burst=20 nodelay; # Save configuration (NPM auto-reloads) ``` --- ## XIII. Advanced Configurations ### High Availability Setup (Future Expansion) If you need HA in the future: ``` ┌─────────────────────────────────────────────────────────────────┐ │ High Availability Architecture │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Load Balancer (HAProxy/Keepalived) │ │ │ │ │ ┌───────────┴───────────┐ │ │ │ │ │ │ ┌────▼─────┐ ┌─────▼────┐ │ │ │ n8n-01 │ │ n8n-02 │ │ │ │ CT: 113 │ │ CT: 114 │ │ │ └────┬─────┘ └─────┬────┘ │ │ │ │ │ │ └───────────┬───────────┘ │ │ │ │ │ ┌────────▼────────┐ │ │ │ PostgreSQL HA │ │ │ │ (Replication) │ │ │ └─────────────────┘ │ │ │ │ Requirements: │ │ - Shared PostgreSQL with streaming replication │ │ - Queue mode enabled (requires Redis) │ │ - Session persistence at load balancer │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### Custom Node Development ```bash # Setup development environment # On CT 113 or separate dev container # Install n8n development tools npm install -g n8n-node-dev # Create custom node n8n-node-dev new # Follow prompts to create node # Test node locally # Package and install to production n8n ``` --- ## XIV. Integration Examples ### Example 1: GitLab CI/CD Automation ``` Workflow: "GitLab Deploy Notification" Trigger: GitLab Webhook (on pipeline completion) │ ├─ Filter: status == "success" │ ├─ Get Deployment Details (GitLab API) │ ├─ Update Netbox (HTTP Request to VM 103) │ ├─ Send Slack Notification │ └─ Create Jira Ticket (if production deploy) ``` ### Example 2: Infrastructure Monitoring ``` Workflow: "Service Health Monitor" Trigger: Schedule (every 5 minutes) │ ├─ Check GitLab Health (HTTP Request) │ ├─ Check NPM Status (http://192.168.2.101:81) │ ├─ Check Docker Hub (HTTP Request to VM 100) │ ├─ Query Netbox for Status (HTTP Request) │ ├─ Aggregate Results │ └─ If any failed: ├─ Send Email Alert ├─ Post to Slack └─ Create PagerDuty Incident ``` ### Example 3: Automated Backups ``` Workflow: "Backup Verification" Trigger: Schedule (daily at 3 AM) │ ├─ SSH to each VM/CT │ ├─ Run backup script │ ├─ Verify backup file exists │ ├─ Check backup size (ensure not 0 bytes) │ ├─ Update Google Sheets log │ └─ Send summary email ``` --- ## XV. Performance Optimization ### Tuning PostgreSQL for n8n ```sql -- Run these on PostgreSQL for better performance -- Increase work memory for complex queries ALTER SYSTEM SET work_mem = '50MB'; -- Optimize for SSD (ZFS) ALTER SYSTEM SET random_page_cost = 1.1; -- Enable parallel query execution ALTER SYSTEM SET max_parallel_workers_per_gather = 2; ALTER SYSTEM SET max_parallel_workers = 4; -- Reload configuration SELECT pg_reload_conf(); -- Create indexes for common queries CREATE INDEX IF NOT EXISTS idx_execution_data_workflow_id ON public.execution_entity (workflow_id); CREATE INDEX IF NOT EXISTS idx_execution_data_finished ON public.execution_entity (finished); -- Vacuum and analyze VACUUM ANALYZE; ``` ### n8n Performance Settings ```bash # Edit /opt/n8n/.env # Increase concurrent executions EXECUTIONS_PROCESS=main EXECUTIONS_MODE=regular # Queue mode for high throughput (requires Redis) # EXECUTIONS_MODE=queue # QUEUE_BULL_REDIS_HOST=localhost # QUEUE_BULL_REDIS_PORT=6379 # Increase payload limits N8N_PAYLOAD_SIZE_MAX=32 # Cache settings CACHE_TTL=3600 ``` --- ## XVI. Cost Analysis & Resource Planning ### Resource Usage Projection ``` ┌─────────────────────────────────────────────────────────────────┐ │ Resource Usage Forecast │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Current (0-10 workflows): │ │ ├─ CPU: 5-10% avg (0.1-0.2 vCPU) │ │ ├─ RAM: 600-800 MB │ │ └─ Disk: 5 GB │ │ │ │ Growth (10-50 workflows): │ │ ├─ CPU: 15-25% avg (0.3-0.5 vCPU) │ │ ├─ RAM: 1.5-2 GB │ │ └─ Disk: 15 GB │ │ │ │ Heavy Use (50-100 workflows): │ │ ├─ CPU: 30-50% avg (0.6-1.0 vCPU) │ │ ├─ RAM: 3-4 GB │ │ └─ Disk: 30 GB │ │ │ │ Scaling Recommendations: │ │ - At 50 workflows: Increase RAM to 6 GB │ │ - At 100 workflows: Increase vCPU to 4 cores │ │ - At 200 workflows: Consider queue mode + Redis │ │ - At 500+ workflows: Migrate to dedicated VM or HA setup │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## XVII. Documentation & Knowledge Base ### Essential Resources ``` ┌─────────────────────────────────────────────────────────────────┐ │ N8N Documentation Links │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Official Documentation: │ │ └─ https://docs.n8n.io/ │ │ │ │ Community Forum: │ │ └─ https://community.n8n.io/ │ │ │ │ GitHub Repository: │ │ └─ https://github.com/n8n-io/n8n │ │ │ │ Workflow Templates: │ │ └─ https://n8n.io/workflows │ │ │ │ YouTube Channel: │ │ └─ https://www.youtube.com/@n8n-io │ │ │ │ Integration Nodes: │ │ └─ https://docs.n8n.io/integrations/ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### Internal Documentation Create these documents in your homelab repo: 1. **/home/jramos/homelab/docs/n8n-workflows.md** - Document all active workflows - Purpose, trigger, dependencies - Maintenance notes 2. **/home/jramos/homelab/docs/n8n-credentials.md** (encrypted) - Credential inventory - Service API endpoints - Renewal/rotation schedule 3. **/home/jramos/homelab/docs/n8n-runbook.md** - Emergency procedures - Escalation contacts - Restore procedures --- ## XVIII. Migration Path from SQLite (If Needed) If you start with SQLite and later migrate to PostgreSQL: ```bash # Export from SQLite n8n export:workflow --all --output=/tmp/workflows.json n8n export:credentials --all --output=/tmp/credentials.json # Stop n8n systemctl stop n8n # Update .env to use PostgreSQL # (already configured in Phase 5) # Import to PostgreSQL n8n import:workflow --input=/tmp/workflows.json n8n import:credentials --input=/tmp/credentials.json # Start n8n systemctl start n8n ``` --- ## XIX. Final Architecture Diagram ``` ╔══════════════════════════════════════════════════════════════════╗ ║ ║ ║ N8N HOMELAB INFRASTRUCTURE - FINAL STATE ║ ║ ║ ╠══════════════════════════════════════════════════════════════════╣ ║ ║ ║ Internet ║ ║ │ ║ ║ ▼ ║ ║ ┌──────────────────┐ ║ ║ │ Router/Firewall │ ║ ║ │ 192.168.2.1 │ ║ ║ │ Port 443 → 101 │ ║ ║ └────────┬─────────┘ ║ ║ │ ║ ║ ┌───────────────────────┼─────────────────────────┐ ║ ║ │ Proxmox Node: serviceslab │ ║ ║ │ 192.168.2.100/24 (vmbr0) │ ║ ║ │ │ │ ║ ║ │ ┌───────────────────┼───────────────────────┐ │ ║ ║ │ │ │ │ │ ║ ║ │ │ ┌────────────────▼──────┐ ┌──────────▼┐ │ ║ ║ │ │ │ NPM (CT 102) │ │ n8n │ │ ║ ║ │ │ │ 192.168.2.101 │ │ (CT 113) │ │ ║ ║ │ │ ├───────────────────────┤ │ .113 │ │ ║ ║ │ │ │ - SSL Termination │ │ │ │ ║ ║ │ │ │ - Reverse Proxy │───┤ n8n App │ │ ║ ║ │ │ │ - Let's Encrypt │ │ :5678 │ │ ║ ║ │ │ │ - Rate Limiting │ │ │ │ ║ ║ │ │ └───────────────────────┘ │ Postgres │ │ ║ ║ │ │ │ :5432 │ │ ║ ║ │ │ └─────┬─────┘ │ ║ ║ │ │ │ │ ║ ║ │ │ ┌─────────────────────────────────┼─────┐ │ ║ ║ │ │ │ Integration Services │ │ │ ║ ║ │ │ │ │ │ │ ║ ║ │ │ │ ┌──────────┐ ┌──────────┐ │ │ │ ║ ║ │ │ │ │ GitLab │ │ Docker │ │ │ │ ║ ║ │ │ │ │ VM 101 │ │ Hub │ │ │ │ ║ ║ │ │ │ │ 17GB RAM │ │ VM 100 │ │ │ │ ║ ║ │ │ │ └────┬─────┘ └────┬─────┘ │ │ │ ║ ║ │ │ │ │ │ │ │ │ ║ ║ │ │ │ ┌────▼─────┐ ┌────▼─────┐ │ │ │ ║ ║ │ │ │ │ Netbox │ │ Ansible │ │ │ │ ║ ║ │ │ │ │ CT 103 │ │ VM 106 │◄── ─┼─────┘ │ ║ ║ │ │ │ │ IPAM │ │ Control │ │ │ ║ ║ │ │ │ └──────────┘ └──────────┘ │ │ ║ ║ │ │ └───────────────────────────────────────┘ │ ║ ║ │ │ │ ║ ║ │ │ ┌──────────────────────────────────────┐ │ ║ ║ │ │ │ Storage: Vault (ZFS) │ │ ║ ║ │ │ │ 4.36 TB Total / 124 GB Used │ │ ║ ║ │ │ └──────────────────────────────────────┘ │ ║ ║ │ │ │ ║ ║ │ └─────────────────────────────────────────────┘ ║ ║ │ ║ ║ │ Backups: PBS (192.168.2.151) ║ ║ └─────────────────────────────────────────────────────────── ║ ║ ║ ╚══════════════════════════════════════════════════════════════════╝ Workflow Examples: ───────────────── • GitLab Pipeline → n8n → Slack Notification • Schedule → n8n → Ansible Playbook → Email Report • Webhook → n8n → Update Netbox → GitLab Issue • Cron → n8n → Backup Verification → Dashboard Update ``` --- ## XX. Conclusion & Next Steps ### Summary You now have a comprehensive blueprint for deploying n8n in your Proxmox homelab. The LXC container approach provides: - **Efficiency**: Minimal resource overhead (~600 MB RAM, < 5 GB disk) - **Performance**: Near-native execution speed - **Reliability**: PostgreSQL backend with automated backups - **Security**: Reverse proxy with SSL, firewall rules, unprivileged container - **Scalability**: Easy to upgrade resources as workflow count grows - **Integration**: Seamless connection to your existing infrastructure ### Immediate Next Steps 1. **Create CT 113** using Phase 1 instructions 2. **Install PostgreSQL** (Phase 3) 3. **Deploy n8n** (Phase 4-6) 4. **Configure NPM proxy** (Phase 7) 5. **Test connectivity** (Phase 9) 6. **Setup backups** (Phase 10) ### Week 1 Goals - [ ] Complete installation (Phases 1-7) - [ ] Create first workflow (scheduled health check) - [ ] Integrate with GitLab - [ ] Configure backups - [ ] Test disaster recovery ### Month 1 Goals - [ ] Deploy 5-10 production workflows - [ ] Setup monitoring and alerting - [ ] Document all workflows - [ ] Optimize PostgreSQL performance - [ ] Implement advanced security measures ### Future Considerations **3 Months:** - Evaluate workflow performance - Consider queue mode if > 50 workflows - Implement custom nodes if needed **6 Months:** - Review HA requirements - Integrate with additional services - Expand automation scope **1 Year:** - Consider n8n Cloud migration (if needed) - Evaluate container→VM migration - Implement disaster recovery testing --- ## Appendix A: Quick Reference Commands ```bash # Container Management pct list # List all containers pct start 113 # Start n8n container pct stop 113 # Stop n8n container pct enter 113 # Enter container shell # Service Management systemctl status n8n # Check n8n status systemctl restart n8n # Restart n8n journalctl -u n8n -f # Follow n8n logs # Database sudo -u postgres psql -d n8n_db # Connect to database /opt/n8n/backup.sh # Run backup # Health Checks /usr/local/bin/n8n-health-check # Run health check curl http://localhost:5678/healthz # Test n8n API # Updates /opt/n8n/update-n8n.sh # Update n8n apt update && apt upgrade -y # Update system packages # Backups ls -lh /opt/n8n/backups/ # List backups tar -tzf backup.tar.gz | head # View backup contents ``` --- ## Appendix B: Environment Variables Reference ```bash # Core Configuration DB_TYPE=postgresdb # Database type N8N_PROTOCOL=https # Protocol (http/https) N8N_HOST=n8n.yourdomain.com # Public hostname N8N_PORT=5678 # n8n listen port WEBHOOK_URL=https://... # Webhook base URL # Database DB_POSTGRESDB_HOST=localhost DB_POSTGRESDB_PORT=5432 DB_POSTGRESDB_DATABASE=n8n_db DB_POSTGRESDB_USER=n8n_user DB_POSTGRESDB_PASSWORD=... # Execution EXECUTIONS_TIMEOUT=300 # Default timeout (seconds) EXECUTIONS_TIMEOUT_MAX=3600 # Maximum timeout EXECUTIONS_DATA_SAVE_ON_ERROR=all # Save failed executions EXECUTIONS_DATA_SAVE_ON_SUCCESS=all # Save successful # Performance N8N_PAYLOAD_SIZE_MAX=16 # Max payload size (MB) EXECUTIONS_PROCESS=main # Execution mode # Timezone GENERIC_TIMEZONE=America/New_York # Security N8N_ENCRYPTION_KEY=... # Credential encryption key N8N_JWT_AUTH_ACTIVE=true # Use JWT auth ``` --- ## Appendix C: Support & Contact ### Getting Help 1. **N8N Community Forum**: https://community.n8n.io/ 2. **GitHub Issues**: https://github.com/n8n-io/n8n/issues 3. **Discord**: https://discord.gg/n8n 4. **Documentation**: https://docs.n8n.io/ ### This Document - **Location**: /home/jramos/homelab/N8N-SETUP-PLAN.md - **Version**: 1.0 - **Created**: 2025-11-29 - **Last Updated**: 2025-11-29 - **Author**: The Scribe (Homelab Infrastructure) --- **Remember**: This is a living document. Update it as your infrastructure evolves, workflows expand, and requirements change. Good infrastructure documentation is your future self's best friend. Now, let's build something remarkable with n8n. *— The Scribe*