Files
homelab/docs/DECISIONS.md
T
lerko96 cd454b2926 docs(public): populate phase 2 content
Full public/ directory — services, network, decisions, security,
inventory, rebuild sequence, and per-LXC setup guides. Sourced from
wiki. No secrets or WAN IPs included.
2026-04-17 21:23:59 -04:00

4.0 KiB
Raw Blame History

Decisions

Architecture Decision Records (ADR-lite). Key choices and rationale.


D001 — Public/private split: git subtree + push script + pre-push hook

Decision: Public content lives under public/. Push script uses git subtree push --prefix=public to publish it as root to the public remote. Pre-push hook blocks direct pushes that bypass the script.
Why: git-filter-repo is designed for one-time rewrites, not recurring pushes. Separate branches require manual discipline. git subtree is pure git, produces clean history on the public remote, and the script stays two lines.
Status: decided


D002 — Public remote: self-hosted Gitea → GitHub mirror

Decision: Public remote is a self-hosted Gitea instance. Gitea mirrors to GitHub automatically.
Why: Matches existing portfolio site setup. Local workflow only pushes to Gitea; GitHub propagation is transparent. No extra tooling needed.
Constraint: Filtering must be airtight before push — whatever reaches Gitea lands on GitHub within seconds.
Status: decided


D003 — Private remote: private repo on same Gitea instance

Decision: Private remote is a separate private repository on the same self-hosted Gitea instance.
Why: Easiest path — infrastructure already exists, one tool to manage.
Risk: Single point of failure. If Gitea host goes down, both remotes are inaccessible. Accepted for now.
Status: decided


D004 — Shared Postgres + Redis in apps LXC

Decision: Single Postgres instance with multiple databases + single Redis instance, both in the apps LXC. All productivity apps share this infrastructure.
Why: Avoids 15 separate DB containers. A single init script provisions all schemas on first run.
Risk: If Postgres goes down, all productivity apps go down simultaneously.
Status: decided


D005 — AT&T gateway kept in-line (IP Passthrough, not EAP bypass)

Decision: BGW320 stays in-line with IP Passthrough mode (DHCPS-fixed to pfSense WAN MAC). pfSense gets the public IP directly. Gateway WiFi disabled.
Why: AT&T locks 802.1X certificate auth to their gateway hardware. EAP proxy bypass breaks on AT&T firmware updates and only saves 12ms latency. True bridge mode not supported.
Status: decided


D006 — Caddy over NGINX Proxy Manager, with Cloudflare DNS-01

Decision: Caddy with caddy-dns/cloudflare plugin. DNS-01 challenge via Cloudflare API. All *.lerkolabs.com subdomains → 10.2.0.20 in Pi-hole. Caddy terminates SSL, proxies to backends.
Why: Single Caddyfile, auto-cert, no UI overhead. No port 80/443 needed on WAN.
Alternatives: NGINX Proxy Manager (more UI overhead), Traefik (more complex config, same result), self-signed certs (browser warnings).
Status: decided


D007 — WireGuard over OpenVPN

Decision: WireGuard on pfSense, UDP 51820, VPN subnet 10.200.0.0/24. VPN clients get same access as LAN.
Why: Lower latency, better mobile battery life, ~600Mbps on the N100. OpenVPN adds complexity with no advantage here.
Status: decided


D008 — Authentik over Authelia

Decision: Authentik as SSO provider for all services.
Why: Full OIDC provider + forward auth in one. Lets services like Outline, Gitea, and Vikunja use real SSO rather than just a login gate. Authelia only does forward auth.
Status: decided


D009 — Pi-hole in Homelab VLAN (1020), not MGMT

Decision: Pi-hole at 10.2.0.11 in VLAN 1020. Firewall allows port 53 inbound from all VLANs. MGMT VLAN uses pfSense as primary DNS.
Why: Placing Pi-hole in MGMT would require allowing all VLANs to reach MGMT — larger attack surface than filtering DNS traffic from Homelab VLAN.
Status: decided


D010 — Intel N100 for pfSense

Decision: Intel N100 mini PC. 4-core 3.4GHz, ~6W idle. Handles 23Gbps routing, 600900Mbps WireGuard.
Why: Right-sized for 1Gbps fiber with headroom. Raspberry Pi insufficient for 1Gbps + VPN. Full rack server overkill power draw.
Status: decided