Homelab Topology & Service Routing
Three-tier Traefik architecture β browser β service request flow, all hosts and networks, all routed services. State as of 2026-07-06 (post-grizzley-replacement migration complete).
1. Physical Hosts
Public Edge
| Host |
IP |
Role |
| Internet / Public DNS |
47.156.104.83 (apex tophermayor.com) |
Cloudflare authoritative DNS for *.tophermayor.com |
| UCG Ultra |
LAN 192.168.1.1, gateway 192.168.50.1 |
UniFi OS 5.1.x Β· Network App 10.4.57 Β· 3 internal VLANs (10, 30, 50) Β· LAN DNS resolver + DHCP + DNS Policies |
Tier 1 β Public Traefik Ingress (LXC)
| Host |
IP |
Role |
| CT 102 traefik-lxc |
192.168.50.115 (LAN) Β· public IP via UCG Ultra |
Debian 13 LXC Β· traefik:v3.x systemd service Β· 4 entrypoints: web:80, websecure:443, sshclone:2222, metrics:8080 Β· ~70 HTTP routers + 1 TCP router |
Tier 2 β Forwarder Layer
| Host |
IP |
Role |
| VM 9003 ubuntu |
192.168.50.61 |
Ubuntu 26.04 on PVE Β· own Traefik (public ingress) Β· file-provider forwarders (priority 250) Β· 12 non-migrated containers + postgres-shared (kept for 30-day rollback until 2026-08-06) |
Tier 3 β New Primary Compute
| Host |
IP |
Role |
| Grizzley (NEW) |
192.168.50.144 (eth0) Β· 192.168.10.145 (wlan0, VLAN 10) |
Raspberry Pi 5 Β· Ubuntu 26.04 LTS (Resolute Raccoon, arm64) Β· Docker 29.6.1 + Compose 5.3.0 Β· bare Traefik 3.0.0 (systemd) Β· NOPASSWD sudo for bear |
Tier 0 β Storage & Other Compute
| Host |
IP |
Role |
| TrueNAS |
192.168.50.12 |
NFS server Β· mediadata, backups, traefik-certs |
| Ice |
192.168.50.197 |
Pi 4 Β· Ubuntu 26.04 Β· Immich + traefik-ice + jump host to PVE |
| Panda |
192.168.30.196 (VLAN 30) |
Home Assistant OS (smart home control plane) |
| PVE hypervisor |
192.168.50.11 |
Proxmox VE Β· runs CT 102, CT 104, CT 200, VM 9003 (ubuntu) |
| CT 104 (this machine) |
192.168.50.x |
Where the docs webapp is hosted Β· LXC container on PVE Β· Hermes default profile |
| CT 200 media-lxc |
192.168.50.200 |
6c/16GB Β· GPU-passthrough GTX 1080 Β· Jellyfin (29.7x HEVC 1080p transcode) |
2. Networks & VLANs
| Network |
Range |
Notes |
| Production (VLAN 50) |
192.168.50.0/24 |
gateway 192.168.50.1 Β· 7 clients (truenas, traefik-CT102, termix-CT126, grizzley, hermes-pve-CT104, VectorPro, CT200) |
| Family / WiFi (VLAN 10) |
192.168.10.0/24 |
grizzley wlan0 .145 lives here |
| Home Assistant (VLAN 30) |
192.168.30.0/24 |
panda .196 |
| DMZ / Staging (VLAN 40) |
192.168.40.0/24 |
|
| Mgmt / UGC Ultra (VLAN 1) |
192.168.1.0/24 |
UniFi controller at .1 |
| docker bridge (proxy-net) |
172.19.0.0/16 |
grizzley containers & postgres-shared |
3. Three-Tier Traefik β Static + Dynamic Config Map
Tier 1 β CT 102 (public Traefik)
/etc/traefik/traefik.yml (STATIC)
entryPoints:
web:80 β redirectβwebsecure
websecure:443 β websecure routers
sshclone:2222 β TCP forwarder β gitea
metrics:8080
/etc/traefik/dynamic/ (file-provider, hot-reload)
βββ conf.yml β wildcard cert + dashboards
βββ ubuntu.yml β Host(ubuntu.tophermayor.com) β 192.168.50.61 (legacy)
βββ gitea-ssh-forwarder.yml β TCP :2222 β 192.168.50.144:2222
βββ grizzley-backup-routers.yml
βββ splash-fallbacks.yml β priority-1 fallbacks
βββ media-lxc.yml, pve.yml, truenas.yml, termix.yml, ... β 20+ more
βββ 95 DNS policies via UniFi integration v1
Tier 2 β ubuntu Traefik (FORWARDER ONLY post-migration)
/etc/traefik/dynamic/
βββ grizzley-auth-forwarder.yml β Host(auth.tophermayor.com) + PathPrefix(/outpost) β grizzley:9000
βββ grizzley-migrated-forwarder.yml β gitea/vaultwarden/navidrome/slskd/homepage β grizzley
βββ upstream-ingress.yml β homepage public routes (repointed to .144)
βββ 12+ other files for ubuntu-local services
Tier 3 β grizzley bare Traefik (systemd, NOT docker)
/etc/traefik/traefik.yml (STATIC)
entryPoints:
websecure:8443 β app routers
metrics:8080 (off by default)
/etc/traefik/dynamic/
βββ authentik.yml β Host(auth.tophermayor.com) β :9000 + PathPrefix(/outpost) β :9000
βββ (each app's compose has its own service exposure via ports:, NOT via traefik labels)
Auth:
CF_DNS_API_TOKEN via
/etc/traefik/dns-token.env (chmod 600).
Cert: Let's Encrypt DNS-01 via Cloudflare (one cert for
auth.tophermayor.com).
4. Request Flow β Browser β Service
4a. Generic Public Request (e.g. https://gitea.tophermayor.com)
[Step 1 β DNS resolution (client side)]
Browser/curl β Cloudflare DNS β gitea.tophermayor.com β tophermayor.com apex
β A 47.156.104.83 (public IP)
[Step 2 β TCP/TLS handshake + HTTP request (Tier 1)]
Browser β UCG Ultra β CT 102 traefik-lxc :443 (websecure entrypoint)
β TLS SNI=gitea.tophermayor.com β wildcard cert from acme.json
β dynamic/ubuntu.yml: Host(gitea.tophermayor.com) β ubuntu-traefik svc (priority 100)
[Step 3 β Forwarder (Tier 2)]
ubuntu Traefik :443 β dynamic/grizzley-migrated-forwarder.yml: grizzley-gitea (priority 250)
β svc gitea-grizzley @ http://192.168.50.144:3000
[Step 4 β Backend (Tier 3)]
grizzley .144:3000 β gitea container (port 3000)
Note: Each service may take additional hops. For auth.tophermayor.com, the backend is :9000 (Authentik). For OIDC forwardAuth on OIDC-protected services, the forwardAuth call hops: CT 102 β ubuntu β grizzley :9000 β outpost.goauthentik.io path.
4b. SSH-clone Request (e.g. git clone gitea.tophermayor.com:2222)
client β Cloudflare β 47.156.104.83:2222
β CT 102 traefik :2222 (sshclone entrypoint, TCP)
β dynamic/gitea-ssh-forwarder.yml: tcp.routers.gitea-ssh-public
β svc gitea-ssh @ 192.168.50.144:2222
β gitea sshd (port 22 in container)
4c. OIDC ForwardAuth on gitea
browser β CT 102 :443
β router: Host(gitea.tophermayor.com) middlewares=[forwardAuth,local-only]
β forwardAuth β http://192.168.50.144:9000/outpost.goauthentik.io/auth/traefik
(hops ubuntu .61 forwarder)
β Authentik :9000 checks session, returns 302 to /application/o/authorize/
β If unauthed: 302 β auth.tophermayor.com/application/o/authorize/?...
β User logs in via Authentik β cookie set β gitea request succeeds
4d. LAN-only request (e.g. https://gitea.local.tophermayor.com)
LAN client β UCG Ultra LAN DNS (192.168.50.1)
β A 47.156.104.83 (or direct to 192.168.50.115 if UniFi has split-horizon)
β CT 102 :443
β router: Host(gitea.local.tophermayor.com) middlewares=[local-only,forwardAuth]
β Same as 4a from Tier 2 onward
5. Per-Service Routing Table
| Hostname |
Tier 1 (CT 102) |
Tier 2 (ubuntu forwarder) |
Tier 3 (grizzley) |
Backend |
Auth |
gitea.tophermayor.com |
ubuntu.yml: gitea-public (100) |
grizzley-migrated-forwarder: grizzley-gitea (250) |
β (direct port) |
192.168.50.144:3000 (gitea) |
forwardAuth β auth.tophermayor.com |
gitea.tophermayor.com:2222 |
TCP: gitea-ssh-public (200) |
β (passthrough) |
β (direct port) |
192.168.50.144:2222 (gitea sshd) |
SSH key (gitea user account) |
vaultwarden.tophermayor.com |
ubuntu.yml: vaultwarden-public (200) |
grizzley-migrated-forwarder: grizzley-vaultwarden (250) |
β (direct port) |
192.168.50.144:80 (vaultwarden) |
forwardAuth (cookie session) |
navidrome.tophermayor.com |
ubuntu.yml: navidrome-public (200) |
grizzley-migrated-forwarder: grizzley-navidrome (250) |
β (direct port) |
192.168.50.144:4533 (navidrome) |
navidrome native session |
slskd.local.tophermayor.com |
ubuntu.yml: sldk-local (200) |
grizzley-migrated-forwarder: grizzley-slskd (250) |
β (direct port) |
192.168.50.144:5030 (slskd) |
slskd native session |
homepage.tophermayor.com |
upstream-ingress.yml: homepage (100) |
β (bypassed, Tier 2βTier 3 direct) |
β (direct port) |
192.168.50.144:3003 (homepage) |
forwardAuth |
homepage.local.tophermayor.com |
upstream-ingress.yml: homepage-local (100) |
β (direct) |
β (direct port) |
192.168.50.144:3003 |
local-only + forwardAuth |
homepage-grizzley.local.tophermayor.com |
upstream-ingress.yml: homepage-grizzley (250) |
grizzley-migrated-forwarder: grizzley-homepage-grizzley (250) |
β (direct port) |
192.168.50.144:3003 |
forwardAuth |
auth.tophermayor.com |
β (CT 102 passes through) |
grizzley-auth-forwarder: grizzley-authentik (100/2001) |
β (direct port) |
192.168.50.144:9000 (authentik) |
Authentik native login |
auth.tophermayor.com/outpost.goauthentik.io/* |
β |
grizzley-auth-forwarder: outpost router (2001) |
β (direct port) |
192.168.50.144:9000 |
β (OIDC forwardAuth endpoint) |
immich.tophermayor.com |
β |
immich route on ubuntu |
β (still on ubuntu) |
192.168.50.61:2283 (immich) |
forwardAuth (immich OIDC) |
jellyfin.tophermayor.com |
ubuntu.yml: jellyfin-public (200) |
β (still on ubuntu) |
β |
192.168.50.200:8096 (jellyfin, media-lxc) |
jellyfin native |
traefik-lxc.lan |
β (own host) |
β |
β |
192.168.50.115:8080 (metrics) |
basic-auth |
media-lxc.lan |
media-lxc.yml routes |
β (still on media-lxc, .200) |
β |
192.168.50.200 |
varies |
6. DNS Layer Details
Cloudflare (public)
Authoritative for
tophermayor.com. 70 records:
- 3 A-records at public IP
47.156.104.83: opencode, termix, apex tophermayor.com
- 10 CNAMEs β apex (auth, cloud, gitea, immich, jellyfin, jellyseerr, jfa, minecraft, n8n, vault)
- ~57 TXT records (
_acme-challenge.* for cert validation, DKIM, etc.)
navidrome.tophermayor.com added 2026-07-06 (was missing β caused the public 502)
UniFi DNS Policies (LAN)
Authoritative for
{host}.lan zones. 95 policies total.
| Record |
Old β New |
Notes |
grizzley.lan |
.84 β .144 |
Stale fixed 2026-07-06 |
ct102.lan |
(missing) β .115 |
Added 2026-07-06 |
ubuntu.lan, ice.lan, pve.lan, truenas.lan, media-lxc.lan |
already correct |
Verified 2026-07-06 |
*.local.tophermayor.com (14 service hostnames) |
.84 β .144 |
aiomanager, aiometadata, aiostreams, komodo, metrics-grizzley, minecraft*, oc-grizz, reccollection, starfield, status, traefik-grizzley, traefik-wildcard |
7. Migration State
Phase A (Authentik) and Phase B (postgres-shared + 5 apps) complete on 2026-07-06. New grizzley (
.144) is the new primary. Public Traefik ingress unchanged: CT 102 β ubuntu (file-provider forwarders) β grizzley (
:8443 LAN-side). 30-day rollback window: ubuntu containers stopped, data preserved on disk, postgres-shared still running until 2026-08-06.
2 stale records intentionally NOT changed: *.pi.tophermayor.com β .84 (wildcard, separate host) and oc-ice.local.tophermayor.com β .84 (OpenCode on ice, not grizzley).