Initial commit: homelab infrastructure wiki

- Full Obsidian vault content
- Host configs (ice, grizzley, ubuntu, proxmox, truenas, panda, hyte)
- Media stack documentation
- Traefik HA setup
- Automation scripts
- Bachelor party planning
This commit is contained in:
Hermes Agent
2026-05-24 16:08:40 -07:00
parent d132442429
commit e4d91aadf9
285 changed files with 30018 additions and 0 deletions

View File

@@ -0,0 +1,363 @@
---
title: Homelab Network Architecture
created: 2026-04-29
updated: 2026-04-29
type: concept
tags: [concept, networking, homelab, traefik, ha]
sources: []
---
# Homelab Network Architecture
Complete traffic flow and routing topology for the homelab cluster. Covers Traefik dual-instance HA, VRRP failover, certificate distribution, Docker network segmentation, and all routing rules.
## Traffic Flow Overview
```
Internet (Cloudflare DNS)
▼ *.tophermayor.com A → home public IP
══════════════════════════════════════════════════════════════════════
VRRP VIP 192.168.50.80/27 (eth0.50) — keepalived
┌─────────────────────────────────────────────────────────────┐
│ PRIMARY: ubuntu traefik (when up) │
│ BACKUP: grizzley traefik-pi (when ubuntu fails) │
└─────────────────────────────────────────────────────────────┘
▼ port 80/443
┌──────────────────────────────────────────────────────────────────┐
│ grizzley traefik-pi │
│ Edge ingress controller (ACME master, Cloudflare DNS challenge) │
│ IP: 192.168.50.84 | Ports: 80,443,2222,8080,19132udp,19134udp │
│ Network: traefik-proxy │
│ Certs: /mnt/truenas/traefik-certs/grizzley (NFS) │
└──────────────────────────────────────────────────────────────────┘
├──[grizzley-local services]──────────────────────────► served directly
│ vaultwarden, uptime-kuma, komodo, homepage,
│ aiostreams, aiomanager, aiometadata,
│ opencode-ice, homeassistant, proxmox, truenas
└──[everything else]────────────────────────────────────► forwarded to ubuntu
(upstream-ingress.yml load-balances to ubuntu:443)
```
## DNS Zones
| Zone | Example | Resolution |
|------|---------|------------|
| Public (`*.tophermayor.com`) | `gitea.tophermayor.com`, `jellyfin.tophermayor.com` | Cloudflare → home public IP |
| Local (`*.local.tophermayor.com`) | `sonarr.local.tophermayor.com`, `proxmox.local.tophermayor.com` | UniFi Controller DHCP/DNS |
Cloudflare proxies all `*.tophermayor.com` — origin IP is hidden, DDoS protection active.
## Network Segmentation
### Physical / VLAN
| Network | Subnet | Gateway | Hosts |
|---------|--------|---------|-------|
| Production (VLAN 50) | 192.168.50.0/24 | 192.168.50.1 | ice, grizzley, ubuntu, proxmox, truenas |
| Default (VLAN 1) | 192.168.1.0/24 | 192.168.1.1 | Management workstations |
| Trusted (VLAN 3) | 192.168.3.0/24 | — | Trusted devices |
| WireGuard VPN | 192.168.4.0/24 | — | VPN clients |
| Docker bridge | 172.16.0.0/12 | — | Container internal networking |
### Docker Networks (ubuntu)
| Network | Driver | Subnet | Connected Services |
|---------|--------|--------|-------------------|
| `proxy-net` | bridge | 172.18.0.0/16 | traefik (primary ingress), homepage-ubuntu |
| `app-net` | bridge | 172.20.0.0/16 | general application containers |
| `uefi-proxynet` | bridge | 172.26.0.0/16 | — |
| `authentik_authentik-internal` | bridge | — | authentik server/worker/redis |
| `monitoring_monitoring-internal` | bridge | — | prometheus, grafana, loki, alertmanager |
| `immich_immich-internal` | bridge | — | immich stack |
| `reccollection-internal` | bridge | — | reccollection stack |
| `ai-subscriptions_default` | bridge | — | ai-subscriptions |
| `infisical_infisical` | bridge | — | infisical stack |
### Docker Networks (grizzley)
| Network | Driver | Connected Services |
|---------|--------|-------------------|
| `traefik-proxy` | bridge | traefik-pi, homepage-grizzley, komodo, aiostreams, aiomanager, aiometadata, vaultwarden, uptime-kuma |
| `aiomanager_default` | bridge | aiomanager stack |
| `aiometadata_aiometadata-internal` | bridge | aiometadata stack |
| `komodo_komodo-internal` | bridge | komodo stack |
| `homepage_default` | bridge | homepage-grizzley |
| `desktop-test_default` | bridge | test containers |
## High Availability (VRRP / Keepalived)
Two Traefik instances provide failover via keepalived VRRP on VLAN 50.
| Parameter | Value |
|-----------|-------|
| Interface | `eth0.50` (VLAN 50) |
| Virtual Router ID | 51 |
| ubuntu priority | **PRIMARY** (higher) |
| grizzley priority | **BACKUP** (90) |
| Virtual IP | `192.168.50.80/27` |
| Auth | PASS (`HomelabH`) |
| Health check | `/etc/keepalived/check_traefik.sh` — 2s interval, fall 2, rise 2 |
When ubuntu Traefik fails health checks, keepalived promotes grizzley to MASTER and the VIP moves to grizzley's interface. Traffic for `*.tophermayor.com` and `*.local.tophermayor.com` then routes to grizzley's traefik-pi (192.168.50.84).
## Certificate Architecture
```
Cloudflare DNS Challenge (grizzley traefik-pi)
ACME writes certs to /etc/traefik/certs/acme.json
▼ (real-time via NFS)
/mnt/truenas/traefik-certs/grizzley (NFS share from TrueNAS)
▼ (read by ubuntu traefik at startup/reread)
ubuntu traefik serves same wildcard certs (*.tophermayor.com)
```
Both instances serve the **same** Cloudflare-issued wildcard certificate (`*.tophermayor.com`) for all public-facing services. The ACME challenge only runs on grizzley — ubuntu syncs certs via NFS.
## Traefik Instance Comparison
| Aspect | ubuntu (PRIMARY) | grizzley (BACKUP / ACME) |
|--------|-----------------|--------------------------|
| Container | `traefik` | `traefik-pi` |
| Image | `traefik:v3.6.7` | `traefik:v3.6.7` |
| IP | 192.168.50.61 | 192.168.50.84 |
| Port 80/443 | Direct | Direct |
| HTTP→HTTPS | ✓ | ✓ |
| Cloudflare ACME | ✗ (reads via NFS) | ✓ (origin) |
| Static configs | `middlewares.yml` | `middlewares.yml` |
| Dynamic configs | 29 files | 4 files |
| Networks | `proxy-net`, `app-net`, `uefi-proxynet` | `traefik-proxy` |
| Metrics port | — | 8080 |
| SSH proxy port | — | 2222 |
| UDP Minecraft | — | 19132, 19134 |
| upstream-ingress | (receives traffic) | forwards to ubuntu |
## Traefik Dynamic Configs
### grizzley (Edge / ACME)
| File | Contents |
|------|---------|
| `pi-routers.yml` | Wildcard cert triggers (`traefik-wildcard.local.tophermayor.com`, `traefik-wildcard.tophermayor.com`) |
| `grizzley-services.yml` | 11 local routers: vaultwarden, uptime-kuma, komodo, homepage, opencode-ice, aiostreams, aiomanager, aiometadata, homeassistant, proxmox, truenas |
| `upstream-ingress.yml` | Forwards all unmatched traffic to ubuntu Traefik (HTTPS 192.168.50.61) |
| `metrics.yml` | Internal metrics endpoints |
| `middlewares.yml` | IP allowlists (`local-only`, `homepage-localonly`), security headers |
### ubuntu (Primary Router)
| File | Contents |
|------|---------|
| `gitea.yml` | gitea.tophermayor.com → gitea:3000 |
| `immich.yml` | immich.tophermayor.com → immich_server:2283 |
| `jellyfin.yml` | jellyfin.tophermayor.com → jellyfin:8096 (rate limit + jellyfin headers) |
| `media-stack.yml` | sonarr, radarr, lidarr, prowlarr, qbittorrent, sabnzbd, readarr, sonarr-anime, radarr-anime, lazylibrarian, nzbdav → via gluetun VPN tunnel |
| `opencode.yml` | opencode.tophermayor.com → host.docker.internal:4096 |
| `proxmox.yml` | proxmox.local.tophermayor.com → https://192.168.50.11:8006 |
| `homepage-widgets.yml` | Internal routes (sonarr-internal, radarr-internal, etc.) → gluetun VPN tunnel |
| `upstream-ingress.yml` | Homepage routes to homepage-ubuntu:3003 and homepage-grizzley:3000 |
| `whisper.yml` | whisper.local.tophermayor.com → faster-whisper-server:8394 |
| `truenas.yml` | truenas.local.tophermayor.com → TrueNAS web UI |
| `navidrome.yml` | navidrome.tophermayor.com |
| `audiobookshelf.yml` | audiobooks.tophermayor.com |
| `calibre-web.yml` | calibre-web.local.tophermayor.com |
| `kavita.yml` | kavita.tophermayor.com |
| `rustfs.yml` | rustfs S3 routes |
| `stremio.yml` | stremio routes |
| `jellyseerr.yml` | jellyseerr.tophermayor.com |
| `comparaison.yml` | comparison service |
| `inventory.yml` | inventory service |
| `cabo-voting.yml` | Cabo voting app |
| `gsd-mcp.yml` | GSD MCP server |
| `ai-subscriptions.yml` | AI subscriptions service |
| `hermes-dashboard.yml` | Hermes dashboard routes |
| `homeassistant.yml` | Home Assistant route |
| `umm.yml` | Unified media manager |
| `middlewares.yml` | Full middleware stack (see below) |
## All Traefik Routes
### grizzley traefik-pi (Local Services)
| Domain | Service | Backend | Middleware | Cert |
|--------|---------|---------|------------|------|
| `vaultwarden.tophermayor.com` | vaultwarden | vaultwarden:80 | — | cloudflare |
| `status.tophermayor.com` | uptime-kuma | uptime-kuma:3001 | — | cloudflare |
| `komodo.local.tophermayor.com` | komodo | komodo:9120 | — | cloudflare |
| `homepage.local.tophermayor.com` | homepage | homepage-grizzley:3000 | homepage-localonly | cloudflare |
| `opencode-ice.local.tophermayor.com` | opencode-ice | 192.168.50.197:4096 | local-only | cloudflare |
| `aiostreams.tophermayor.com` | aiostreams | aiostreams:3002 | — | cloudflare |
| `aiomanager.tophermayor.com` | aiomanager | aiomanager:1610 | — | cloudflare |
| `aiometadata.tophermayor.com` | aiometadata | aiometadata:1337 | — | cloudflare |
| `ha.tophermayor.com` | homeassistant | 192.168.30.196:8123 | — | cloudflare |
| `proxmox.local.tophermayor.com` | proxmox | 192.168.50.11:8006 | local-only | cloudflare |
| `truenas.local.tophermayor.com` | truenas | 192.168.50.12:8080 | local-only | cloudflare |
| `traefik-grizzley.local.tophermayor.com` | dashboard | api@internal | local-only | cloudflare |
| `metrics-grizzley.local.tophermayor.com` | metrics | api@internal | local-only | cloudflare |
### grizzley traefik-pi (Upstream → ubuntu)
Traffic NOT matched above is forwarded via `upstream-ingress.yml`:
| Rule | Target |
|------|--------|
| `HostRegexp(^[a-z0-9-]+\.local\.tophermayor\.com$) && !homepage && !traefik-grizzley && !metrics-grizzley && !traefik-wildcard && !opencode-ice` | → ubuntu:443 |
| `HostRegexp(^[a-z0-9-]+\.tophermayor\.com$) && !traefik-wildcard` | → ubuntu:443 |
### ubuntu traefik (Public Routes — *.tophermayor.com)
| Domain | Backend | Middleware |
|--------|---------|------------|
| `gitea.tophermayor.com` | gitea:3000 | homelab-public |
| `immich.tophermayor.com` | immich_server:2283 | homelab-public |
| `jellyfin.tophermayor.com` | jellyfin:8096 | ratelimit, jellyfin-headers |
| `audiobooks.tophermayor.com` | audiobookshelf | homelab-public |
| `navidrome.tophermayor.com` | navidrome | homelab-public |
| `kavita.tophermayor.com` | kavita:5000 | homelab-public |
| `opencode.tophermayor.com` | host.docker.internal:4096 | local-only, opencode-streaming, opencode-cors |
| `ha.tophermayor.com` | 192.168.30.196:8123 | (see homeassistant.yml) |
| `jellyseerr.tophermayor.com` | jellyseerr | homelab-public |
### ubuntu traefik (Local Routes — *.local.tophermayor.com)
| Domain | Backend | Middleware | Notes |
|--------|---------|------------|-------|
| `sonarr.local.tophermayor.com` | gluetun:8989 | local-only | Via VPN tunnel |
| `radarr.local.tophermayor.com` | gluetun:7878 | local-only | Via VPN tunnel |
| `lidarr.local.tophermayor.com` | gluetun:8686 | local-only | Via VPN tunnel |
| `sabnzbd.local.tophermayor.com` | gluetun:8080 | local-only | Via VPN tunnel |
| `qbittorrent.local.tophermayor.com` | qbittorrent | local-only | |
| `prowlarr.local.tophermayor.com` | prowlarr | local-only | |
| `readarr.local.tophermayor.com` | readarr | local-only | |
| `sonarr-anime.local.tophermayor.com` | sonarr-anime | local-only | Via VPN tunnel |
| `radarr-anime.local.tophermayor.com` | radarr-anime | local-only | Via VPN tunnel |
| `flaresolverr.local.tophermayor.com` | flaresolverr | local-only | |
| `bazarr.local.tophermayor.com` | bazarr:6767 | local-only | |
| `lazylibrarian.local.tophermayor.com` | lazylibrarian | local-only | |
| `nzbdav.local.tophermayor.com` | nzbdav | local-only | |
| `calibre-web.local.tophermayor.com` | calibre-web:8083 | local-only | |
| `stremio.local.tophermayor.com` | stremio-server | local-only | |
| `proxmox.local.tophermayor.com` | 192.168.50.11:8006 | proxmox-headers, local-only | |
| `truenas.local.tophermayor.com` | 192.168.50.12:8080 | local-only | |
| `opencode-ice.local.tophermayor.com` | 192.168.50.197:4096 | local-only | |
| `whisper.local.tophermayor.com` | faster-whisper-server:8394 | local-only | |
| `traefik.local.tophermayor.com` | api@internal | local-only | Dashboard |
### Internal Widget Routes (sonarr-internal, etc.)
These are `*-internal.local.tophermayor.com` routes for Homepage widgets, accessible only inside the network via the gluetun VPN tunnel. From `homepage-widgets.yml`:
| Internal Domain | Backend (via gluetun) |
|-----------------|----------------------|
| `sonarr-internal.local.tophermayor.com` | gluetun:8989 |
| `radarr-internal.local.tophermayor.com` | gluetun:7878 |
| `lidarr-internal.local.tophermayor.com` | gluetun:8686 |
| `sabnzbd-internal.local.tophermayor.com` | gluetun:8080 |
| `seerr-internal.local.tophermayor.com` | seerr:5055 |
| `jellyfin-internal.local.tophermayor.com` | jellyfin:8096 |
| `prometheus-internal.local.tophermayor.com` | prometheus:9090 |
### Special Protocols
| Protocol | Port | Host | Purpose |
|----------|------|------|---------|
| HTTP→HTTPS | 80 | grizzley | Redirects to 443 |
| HTTPS | 443 | grizzley | All TLS traffic |
| QUIC/HTTP3 | 443/udp | grizzley | HTTP3 support |
| Traefik metrics | 8080 | grizzley | Prometheus scraping |
| Gitea SSH proxy | 2222 | grizzley | → ubuntu:2222 |
| Minecraft Bedrock | 19132/udp | grizzley | Bedrock server (standby) |
| Minecraft Bedrock | 19134/udp | grizzley | Bedrock server (sison) |
## Middleware Chains (ubuntu)
### homelab-public
Applied to: gitea, immich, audiobookshelf, navidrome, kavita, jellyseerr, etc.
```
chain: [compress, security-headers, buffering, ratelimit]
```
### Security Headers
Applied to most services:
```yaml
browserXssFilter: true
contentTypeNosniff: true
forceSTSHeader: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000 # 1 year
customFrameOptionsValue: SAMEORIGIN
```
### Jellyfin-specific Headers
Adds CSP allowing jsDelivr CDN for the Ultrachromic theme:
```yaml
contentSecurityPolicy: "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; ..."
```
### Authentik ForwardAuth (SSO)
Applied to: sonarr, radarr, lidarr, prowlarr, bazarr, sabnzbd, transmission, qbittorrent, flaresolverr, jellyseerr, listsync, dockge, it-tools, bentopdf, code-ai, and more.
Each service has its own middleware with `X-authentik-host` query param:
```
http://authentik-server:9000/outpost.goauthentik.io/auth/traefik?X-authentik-host=<domain>
```
### local-only IP Allowlist
```yaml
sourceRange:
- 127.0.0.1/32
- 192.168.50.0/24 # Production
- 192.168.1.0/24 # Management
- 192.168.3.0/24 # Trusted
- 192.168.4.0/24 # WireGuard VPN
- 172.16.0.0/12 # Docker
- 10.0.0.0/8 # VPN/Docker
```
### Rate Limiting
```yaml
average: 100
burst: 50
```
## VPN Tunnel (gluetun)
Media automation services route through **gluetun** VPN container for privacy when connecting to torrent/indexer services:
- sonarr → gluetun:8989
- radarr → gluetun:7878
- lidarr → gluetun:8686
- sabnzbd → gluetun:8080
gluetun ports: 8000, 8388, 8888 (TCP), 8388 (UDP) — exposed on ubuntu's Docker network.
## SSH Routing
Gitea SSH is proxied through grizzley:
```
Internet → grizzley:2222 (SNI * → any)
→ forwards to ubuntu:2222
→ gitea container handles git SSH protocol
```
## UniFi Controller
Network services (DHCP, DNS, VLAN tagging) managed by UniFi Controller at 192.168.1.1 (or similar). All internal DNS for `*.local.tophermayor.com` resolves through the UniFi DNS forwarder.
## Related
- [[traefik]] — Traefik entity page
- [[grizzley]] — RPi5 edge node (ACME master, backup ingress)
- [[ubuntu]] — Primary Docker host (primary ingress router)
- [[truenas]] — NFS storage for cert sync
- [[traefik-ha]] — HA concept page
- [[homepage]] — Dashboard services with widget routes
- [[authentik]] — SSO identity provider
- [[sso-authentik]] — SSO configuration details