exzentcg.com Homelab Infrastructure Proposal¶
Project: Self-Hosted Business Application Platform Domain: exzentcg.com Infrastructure: Proxmox Homelab — 192.168.0.200 Prepared: April 2026
Executive Summary¶
This document proposes a secure, incrementally deployable homelab architecture to host business applications under the exzentcg.com domain. The design prioritises simplicity without sacrificing meaningful security — using Cloudflare's free-tier Zero Trust and Tunnel products as the external security boundary, and Proxmox's built-in Datacenter Firewall for internal network micro-segmentation.
The architecture is structured in two phases. Phase 1 delivers a production-ready n8n automation platform accessible at n8n.exzentcg.com. Phase 2 expands the platform to host additional business applications as the need arises, reusing all infrastructure built in Phase 1.
No additional hardware is required. No physical firewall appliances or router reconfigurations are needed. No inbound ports are opened on the home router at any point.
Network Diagram¶
graph TB
users["Users / Customers"]
external["External Services"]
subgraph CF["Cloudflare Edge"]
dns["DNS"] --> waf["WAF + DDoS"] --> zta["Zero Trust Access"] --> tunnel_cf["Tunnel"]
end
subgraph HOME["Home LAN .0.0/24"]
router["Router .0.1"]
subgraph PVE["Proxmox .0.200 — Firewall"]
subgraph EDGE["edge-gateway .0.51"]
cloudflared["cloudflared"]
npm["Nginx Proxy Manager"]
end
subgraph N8N["n8n-app .0.52"]
n8n_svc["n8n :5678"]
end
subgraph BIZ["biz-app .0.53 — Phase 2"]
biz_svc["App :PORT"]
end
end
end
desktop["Desktop .0.16 — Admin"]
apis["Internet APIs"]
users --> dns
external --> dns
tunnel_cf -->|Encrypted Tunnel| cloudflared
cloudflared --> npm
npm -->|:5678| n8n_svc
npm -.->|Phase 2| biz_svc
router ---|DNS| EDGE
router ---|DNS| N8N
n8n_svc -->|:443 outbound| apis
desktop -->|:8006 + SSH| PVE
style CF fill:#fff3e0,stroke:#ff9800
style HOME fill:#e3f2fd,stroke:#1565c0
style PVE fill:#fff8e1,stroke:#f9a825
style EDGE fill:#e8f5e9,stroke:#43a047
style N8N fill:#e8f5e9,stroke:#43a047
style BIZ fill:#f3f3f3,stroke:#999,stroke-dasharray: 5 5
Lateral movement blocked: Proxmox firewall DROP rules prevent n8n-app and biz-app from reaching Desktop (.0.16), Router (.0.1), Proxmox Host (.0.200), or any other LAN device. See firewall rule tables below for details.
Goals and Constraints¶
Goals¶
- Host
n8n.exzentcg.com(workflow automation) accessible over the internet under a custom domain. - Prevent any compromised container from reaching the home LAN, personal desktop (
192.168.0.16), or the Proxmox management interface (192.168.0.200:8006). - Maintain a simple, low-overhead setup with no dedicated firewall VM.
- Build a repeatable pattern so future business apps can be added with minimal rework.
Constraints¶
- Single physical host running Proxmox (192.168.0.200).
- Home network is flat (
192.168.0.0/24) — no VLAN separation. - No pfSense/OPNsense VM (complexity trade-off accepted for Phase 1).
- All containers will live on
vmbr0(the existing Proxmox bridge).
Architecture Overview¶
The design employs a "Hard Shell, Controlled Interior" model:
- Cloudflare acts as the external security boundary (DDoS, WAF, Zero Trust identity gate, DNS proxy).
- Cloudflare Tunnel (cloudflared) provides an outbound-only encrypted pipe — no inbound ports are ever opened on the home router.
- Nginx Proxy Manager (NPM) on a dedicated
edge-gatewayLXC acts as the single internal ingress point, forwarding subdomains to the correct app container. All Cloudflare Tunnel traffic terminates at NPM — never directly at app containers. - Proxmox Datacenter Firewall enforces micro-segmentation at the hypervisor level, preventing lateral movement from any app container to the home LAN.
- Tailscale provides remote admin access to the Proxmox host, independent of Cloudflare. The admin laptop's Tailscale IP is added to the
admin_desktopIP set so the same firewall rules apply whether admin is on-LAN or remote. This is deliberate: a Cloudflare outage must not lock the operator out of the hypervisor.
The key security principle: even if n8n is fully compromised, the attacker is isolated inside the n8n-app container with no path to your desktop, router, or Proxmox host.
IP and Component Layout¶
All LXCs reside on vmbr0 within the existing 192.168.0.0/24 subnet. Static IPs should be assigned via Proxmox container network config (or DHCP reservation on the router).
| Component | IP Address | Role |
|---|---|---|
| Home Router | 192.168.0.1 | Default gateway, DNS forwarder |
| Proxmox Host | 192.168.0.200 | Hypervisor — management only |
| Desktop (Admin) | 192.168.0.16 | Only trusted admin machine |
| LXC: edge-gateway | 192.168.0.51 | cloudflared + Nginx Proxy Manager |
| LXC: n8n-app | 192.168.0.52 | n8n automation platform (Phase 1) |
| LXC: biz-app | 192.168.0.53 | Business app — ERPNext or similar (Phase 2) |
Phase 1: n8n Deployment¶
Step 1 — Create LXC: edge-gateway (192.168.0.51)¶
This is the single container that faces the internet via Cloudflare. It runs two services, both deployed as a single Docker Compose stack under /opt/edge-gateway/:
- cloudflared: Maintains an outbound TLS tunnel to Cloudflare's edge. Cloudflare routes
n8n.exzentcg.comtraffic into this tunnel. Runs withnetwork_mode: hostso it can reach NPM's localhost bind. - Nginx Proxy Manager (NPM): Receives HTTP/S traffic from cloudflared and proxies it to the correct internal container IP and port. Binds only to
127.0.0.1:80/443(cloudflared path) and192.168.0.51:81(admin panel) — never to0.0.0.0, providing a second layer of defence behind the Proxmox firewall.
Single compose file = single backup target (/opt/edge-gateway/) containing the compose file, NPM data, Let's Encrypt certs, and the tunnel token in .env.
Recommended LXC specs: Debian 12, 1 vCPU, 1 GB RAM, 4 GB disk. (RAM bumped from the original 512 MB to accommodate cloudflared now running as a Docker container alongside NPM rather than as a native apt package.)
Tunnel ingress rule (configured in Cloudflare dashboard):
n8n.exzentcg.com → http://192.168.0.51:80
Important: The tunnel target is NPM on the edge-gateway (
192.168.0.51:80), not the n8n container directly. NPM then proxies to192.168.0.52:5678. This keeps NPM as the single internal routing point for all subdomains — consistent for Phase 1 and Phase 2.
NPM proxy host configuration:
Source: n8n.exzentcg.com
Forward to: http://192.168.0.52:5678
Step 2 — Create LXC: n8n-app (192.168.0.52)¶
This container runs n8n as a Docker Compose service under /opt/n8n/, matching the edge-gateway pattern.
Recommended LXC specs: Debian 12, 2 vCPU, 2 GB RAM, 10 GB disk.
n8n binds to 192.168.0.52:5678 (the LXC's LAN IP), not 0.0.0.0. Proxmox firewall rules additionally ensure only edge-gateway can reach this port.
Required n8n environment variables:
N8N_HOST=n8n.exzentcg.com
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.exzentcg.com
NODES_EXCLUDE=["n8n-nodes-base.executeCommand"]
N8N_ENCRYPTION_KEY=<generated once, stored in .env and password manager>
GENERIC_TIMEZONE=Asia/Singapore
TZ=Asia/Singapore
Image pinning: n8nio/n8n:<version> (not :latest). Upgrades happen deliberately on the weekly maintenance window, not via auto-pull.
Security note — Execute Command node: n8n's
Execute Commandnode (CVE CVSS 9.9) allows authenticated users to run arbitrary OS commands on the host. Disabling it viaNODES_EXCLUDEeliminates this attack vector entirely.Critical — N8N_ENCRYPTION_KEY: n8n encrypts every stored credential (API keys, OAuth tokens, DB passwords) with this key. It is generated once with
openssl rand -hex 32before n8n is first started, and stored in both/opt/n8n/.env(chmod 600) and a password manager. Losing this key is equivalent to losing every credential n8n holds — it must be backed up separately from the n8n data volume.
Step 3 — Cloudflare Zero Trust Access Policy¶
For a non-public admin tool like n8n, an access policy is mandatory.
In Cloudflare: Zero Trust > Access > Applications:
- Add application for
n8n.exzentcg.com. - Session duration: 24 hours.
- Policy rule: Allow where
Emailsequals[your email]. - Authentication method: One-time PIN (email) or connect an identity provider (Google, GitHub).
This means any browser hitting n8n.exzentcg.com is challenged by Cloudflare before the tunnel even forwards the request. The n8n login screen is a secondary layer behind this.
For webhook endpoints (Stripe, Telegram, etc.): Create a separate bypass policy for the path /webhook/* so external services can POST without the identity gate. The main UI paths (/, /workflows, /credentials) remain protected.
Proxmox Firewall Configuration¶
The firewall is configured at three levels: Datacenter > Node > LXC. All three must be enabled for rules to take effect.
⚠️ Critical: Rule ordering. Proxmox evaluates firewall rules top-to-bottom, first match wins. In every rule set below, DROP rules for
lan_subnetmust be placed above any ALLOW-to-any rules. If the ALLOW-to-any on port 443 is evaluated first, traffic destined for a LAN IP on port 443 will be permitted — defeating lateral movement prevention. Always verify rule order after editing.
Pre-requisite: Lockout Prevention¶
Before enabling the Datacenter-level firewall, create these rules on the Node (pve) firewall — failure to do this will lock you out of the Proxmox WebUI:
| # | Direction | Action | Source | Port | Protocol | Comment |
|---|---|---|---|---|---|---|
| 1 | IN | ACCEPT | 192.168.0.16 | 8006 | TCP | Admin WebUI access |
| 2 | IN | ACCEPT | 192.168.0.16 | 22 | TCP | Admin SSH access |
| 3 | IN | DROP | any | 8006 | TCP | Block all others from WebUI |
Proxmox Host: Datacenter IP Sets¶
Define reusable IP sets in Datacenter > Firewall > IPSet:
| IP Set Name | Value | Purpose |
|---|---|---|
admin_desktop |
192.168.0.16, <laptop_tailscale_ip> |
Trusted admin machines (LAN desktop + Tailscale laptop) |
lan_subnet |
192.168.0.0/24 | Entire home LAN |
router_gw |
192.168.0.1 | Default gateway / DNS |
edge_gw |
192.168.0.51 | edge-gateway LXC |
LXC: edge-gateway Firewall Rules¶
| # | Direction | Action | Source/Dest | Port | Protocol | Comment |
|---|---|---|---|---|---|---|
| 1 | IN | ACCEPT | 127.0.0.1 | 80, 443 | TCP | cloudflared (local) → NPM |
| 2 | IN | ACCEPT | admin_desktop |
81 | TCP | NPM admin panel from desktop only |
| 3 | IN | DROP | any | any | any | Block all other inbound |
| 4 | OUT | ACCEPT | router_gw |
53 | UDP/TCP | DNS resolution |
| 5 | OUT | ACCEPT | 192.168.0.52 | 5678 | TCP | Proxy to n8n-app |
| 6 | OUT | ACCEPT | 192.168.0.53 | any | TCP | Proxy to biz-app (Phase 2) |
| 7 | OUT | DROP | lan_subnet |
any | any | Block all other LAN access |
| 8 | OUT | ACCEPT | any | 443 | TCP | cloudflared tunnel + Cloudflare API |
| 9 | OUT | ACCEPT | any | 80 | TCP | HTTP fallback (package updates) |
Change from previous version: Inbound rule #1 is now scoped to
127.0.0.1(localhost) because cloudflared runs on the same container and connects to NPM locally. This prevents any other LAN device from reaching NPM's HTTP ports. Rule #2 allows desktop-only access to NPM's admin panel (port 81).
LXC: n8n-app Firewall Rules¶
| # | Direction | Action | Source/Dest | Port | Protocol | Comment |
|---|---|---|---|---|---|---|
| 1 | IN | ACCEPT | edge_gw |
5678 | TCP | Only proxy may reach n8n |
| 2 | IN | ACCEPT | admin_desktop |
22 | TCP | SSH from admin desktop |
| 3 | IN | DROP | any | any | any | Block all other inbound |
| 4 | OUT | ACCEPT | router_gw |
53 | UDP/TCP | DNS resolution |
| 5 | OUT | DROP | lan_subnet |
any | any | Lateral movement prevention |
| 6 | OUT | ACCEPT | any | 443 | TCP | n8n calling external APIs (HTTPS) |
Changes from previous version:
- Outbound port 80 removed. Modern APIs are HTTPS-only. If a specific integration requires HTTP, add it as a targeted rule for that destination only.
- Rule #5 (DROP
lan_subnet) is ordered above rule #6 (ALLOW any:443). This is critical — it ensures traffic to192.168.0.x:443is dropped before the allow-to-any rule can match it. This closes the DNS rebinding edge case where a malicious domain resolves to a LAN IP.- SSH access added from admin desktop (rule #2) for maintenance.
Traffic Flow Summary¶
External User Visiting n8n.exzentcg.com¶
User Browser
→ Cloudflare DNS resolves to Cloudflare IP (home IP never exposed)
→ Cloudflare WAF + DDoS scrubbing
→ Cloudflare Zero Trust: identity challenge (email PIN / OAuth)
→ Cloudflare Tunnel: encrypted outbound tunnel to edge-gateway LXC
→ cloudflared (192.168.0.51) forwards to localhost NPM
→ Nginx Proxy Manager: routes n8n.exzentcg.com to 192.168.0.52:5678
→ n8n application
→ Response follows same path in reverse
n8n Calling an External API (e.g., Google Sheets)¶
n8n-app (192.168.0.52)
→ Proxmox firewall: DROP lan_subnet check — destination is not LAN, skip
→ Proxmox firewall: ALLOW outbound 443 to internet
→ Router (192.168.0.1)
→ Internet → Google API
→ Response returns to n8n-app
n8n Attempting to Reach Desktop (Attack Scenario)¶
Compromised n8n-app (192.168.0.52)
→ Attempts connection to 192.168.0.16 (any port, including 443)
→ Proxmox firewall: rule #5 DROP (lan_subnet) matched BEFORE rule #6 (allow any:443)
→ Connection never established ✓
DNS Rebinding Attack Scenario¶
Compromised n8n-app (192.168.0.52)
→ Resolves attacker-controlled domain to 192.168.0.16
→ Attempts HTTPS connection to 192.168.0.16:443 via the malicious domain
→ Proxmox firewall: rule #5 DROP (lan_subnet) matches destination IP regardless of DNS name
→ Connection never established ✓
Phase 2: Adding Business Applications¶
When ready to add a business app (e.g., ERPNext, custom web app), the process follows the established pattern:
- Create LXC: biz-app (192.168.0.53) with the same firewall template as
n8n-app(adjust inbound port for the app). - Add NPM proxy host on
edge-gateway:app.exzentcg.com → http://192.168.0.53:PORT. - Add Cloudflare Tunnel ingress rule:
app.exzentcg.com → http://192.168.0.51:80(always through NPM — this is the standard). - Add Cloudflare Access policy for
app.exzentcg.com(public or employee-only, as required). - Add edge-gateway firewall rule:
OUT ACCEPT 192.168.0.53:<PORT>(insert before thelan_subnetDROP rule). - If a database container is needed: create a DB LXC (
192.168.0.54), allow inbound connections only from192.168.0.53on the DB port, block all other LAN and internet access.
Standard: All Cloudflare Tunnel ingress rules must target NPM on
192.168.0.51:80. Never point a tunnel directly at an app container. NPM is the single internal routing point.
No changes to existing Phase 1 infrastructure are required.
Security Assessment¶
| Threat | Mitigated By | Residual Risk |
|---|---|---|
| Internet port scanning | Cloudflare Tunnel — no open ports | None |
| DDoS / volumetric attack | Cloudflare WAF + DDoS protection | Low |
| Unauthorised n8n access | Cloudflare Zero Trust Access | Low |
| n8n command execution CVE | NODES_EXCLUDE env var |
None (node disabled) |
| Compromised n8n → LAN pivot | Proxmox per-LXC firewall DROP rules (ordered above ALLOW) | Low |
| DNS rebinding to LAN IPs | DROP lan_subnet evaluated before ALLOW any:443 |
Low |
| Brute-force n8n login | Zero Trust gate before login screen | Low |
| Home IP exposure | Cloudflare proxy (orange cloud DNS) | None |
| Proxmox GUI exposure | Node firewall ALLOW only from admin_desktop IP set (LAN desktop + Tailscale laptop) |
Low |
| NPM admin panel exposure | Inbound restricted to localhost + admin desktop | Low |
| Remote admin dependency on Cloudflare | Tailscale mesh is independent of Cloudflare; outage of one does not lock out the other | None |
Accepted risk: No network-layer IDS/IPS is present (this would require OPNsense in Phase 2+). Application-layer security relies on keeping n8n and its dependencies updated.
Maintenance Checklist¶
- Weekly: Check n8n for available updates; apply in a maintenance window.
- Weekly: Review Cloudflare Access logs for unexpected login attempts.
- Monthly: Review Proxmox firewall logs for anomalous DROP hits.
- Monthly: Snapshot both LXCs in Proxmox before any major changes.
- Monthly: Verify firewall rule ordering has not changed after any Proxmox updates.
- As needed: Rotate n8n credentials and API keys stored in n8n's credential vault.
Future Considerations (Phase 3+)¶
Once multiple business applications are running, migrating from a flat Proxmox Firewall model to an OPNsense virtual firewall with proper VLAN/DMZ segmentation is strongly recommended. OPNsense running as a Proxmox VM provides Suricata-based intrusion detection, centralised firewall management, and a cleaner separation between the home LAN and business workloads — without requiring any additional physical hardware.