Files
homelab/n8n/N8N-SETUP-PLAN.md
Jordan Ramos 779ae2fb24 docs(n8n): enhance setup guide with PostgreSQL 15+ fixes and encryption key validation
Update n8n deployment documentation to prevent three critical issues discovered during troubleshooting:

1. PostgreSQL 15+ Compatibility (Phase 3):
   - Add explicit schema permission grants for public schema
   - Include C.utf8 locale specification for Debian 12 minimal LXC
   - Add permission validation test before proceeding

2. Encryption Key Generation (Phase 5):
   - Add pre-generation validation to prevent literal command strings in .env
   - Include verification steps for 64-character hex key format
   - Document common misconfiguration and remediation steps

3. SSL Termination Architecture (Phase 7):
   - Clarify NPM scheme setting (http backend vs https external)
   - Explain reverse proxy SSL termination pattern
   - Document why https scheme causes 502 Bad Gateway errors

Update CLAUDE_STATUS.md to mark troubleshooting session complete and document deployment success.

These preventive measures ensure clean deployments on PostgreSQL 16 and avoid the 805+ restart crash loops encountered during initial deployment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 08:55:41 -07:00

2185 lines
84 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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: <your-public-ip>
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*