Tags: 0 - Overview 0 - HomeLab Networking-Computer

2026 Homelab Architecture

The environment is organized as a platform with tenants, not a pile of loosely related hosts. Each node has a defined responsibility, and every significant decision is written down as an ADR — treating the lab like a real platform team would treat its engineering roadmap.

Node Layout

AM4 GPU Node — am4-gpu

Bare-metal Kubernetes worker dedicated to GPU inference.

  • Talos Linux (no Proxmox passthrough — see ADR-002)
  • RTX 4070 Ti Super, 16GB VRAM
  • Dual-boots into Bazzite (gaming OS) on a second drive, isolated from the Talos SSD during install
  • Joined the cluster on VLAN 20 (10.10.20.14)

Dell OptiPlex 3080 — Proxmox Host

Runs the Kubernetes control plane and worker VMs, plus the management VM.

VMvCPURAMDiskPurpose
talos-cp-128GB40GBControl plane
talos-worker-1418GB150GBWorkloads
talos-worker-2418GB150GBWorkloads
mgmt-128GB50GBBootstrap box, DNS, Git mirror

Storage split: 256GB NVMe for Proxmox boot/ISOs/scratch; 512GB SATA SSD for all VM disks (~120GB buffer, thin-provisioned). No local backup datastore fits on these drives — backups target an external USB disk or a share on the HP’s spare HDD capacity.

HP EliteDesk — Standalone Media Server

Deliberately kept outside the Kubernetes cluster (see ADR-003). Debian + Docker Compose running Jellyfin, Audiobookshelf, Calibre-Web, and an ARR/acquisition stack behind a VPN.

Network Plan

UniFi (UCG Fiber gateway) + MikroTik CRS310 switch, VLAN-segmented.

VLANNameSubnetPurpose
1Default192.168.1.0/24Household devices
10HomeLab10.10.10.0/24Infra management — Proxmox UI, mgmt VM, talosctl
20Lab-Cluster10.10.20.0/24Kubernetes node network (Talos VMs + AM4 GPU node)
30IoT192.168.2.0/24Pre-existing, unrelated to the lab
40Lab-Media10.10.40.0/24Media server, reachable from Default for streaming

Static assignments on VLAN 20: talos-cp-1 = .11, talos-worker-1 = .12, talos-worker-2 = .13, am4-gpu = .14. Cluster API endpoint: https://10.10.20.11:6443.

Firewall stance today is permissive between VLANs (no lockdown rules yet) — tightening this is a deferred Phase 2 cleanup item.

Access path. Cluster services are not published to the LAN. Machines join a tailnet, and the Tailscale Kubernetes Operator fronts in-cluster services with MagicDNS names and real HTTPS certificates — so reaching the AI gateway from a phone off-network requires an identity, not an open port. LAN-side exposure via Cilium Gateway API + LB-IPAM is a separate, still-pending path (CRDs audited and confirmed absent; Cilium is a rendered manifest rather than a live Helm release, so enabling it means re-rendering the same v1.19.5, not upgrading).

Repository Structure

One monorepo, mirrored GitHub ↔ Gitea on the management VM:

homelab/
├── ROADMAP.md
├── docs/
│   ├── adr/                    # architecture decision records
│   ├── network.md
│   └── runbooks/                # rebuild, restore, recover
├── terraform/
│   ├── proxmox/                 # VM definitions
│   └── modules/
├── talos/
│   ├── machineconfigs/          # generated + patches, secrets via SOPS
│   └── patches/
├── kubernetes/
│   ├── bootstrap/                # ArgoCD install + root app-of-apps
│   ├── platform/                 # cilium, cert-manager, longhorn, vault, kyverno, observability, harbor, arc
│   ├── ai/                       # gpu-operator, vllm/ollama, time-slicing config
│   └── tenants/                  # per-"team" apps, namespaces, AppProjects
├── .github/workflows/
└── media-server/
    └── docker-compose.yml

Secrets: SOPS + age for anything committed to Git; Vault + External Secrets Operator for runtime secrets.

Architecture Decisions

Key ADRs — the full rationale and tradeoffs for each are logged in the build repo’s docs/adr/.

  • ADR-001 — Talos Linux over k3s. Immutable, API-driven, fully declarative node OS: no SSH, no config drift, the entire machine state lives in Git.
  • ADR-002 — Bare-metal GPU node, no Proxmox passthrough. Consumer GPU passthrough is friction with no resume value; bare-metal + the NVIDIA GPU Operator is the pattern production GPU clusters actually use.
  • ADR-003 — Media server stays outside the cluster. Household-critical services shouldn’t depend on infrastructure I deliberately break. The cluster is the disposable experiment plane; the media box is a stable appliance.
  • ADR-004 — Cilium with Gateway API and LB-IPAM. One eBPF-based stack instead of stitching together a CNI, a load-balancer, and an ingress controller separately. Gateway API is the Ingress successor.
  • ADR-005 — GitHub Actions with self-hosted runners (ARC). GitHub stays the public source of truth; builds execute on lab hardware via Actions Runner Controller — autoscaling, ephemeral runner pods, and caching are real platform skills.
  • ADR-006 — GitOps via ArgoCD app-of-apps. Humans never kubectl apply to a tenant namespace; everything enters the cluster through Git.
  • ADR-007 — LiteLLM as a unified inference gateway. One OpenAI-compatible endpoint for every caller, routing between local llama.cpp/Ollama and hosted APIs, with centralized keys, budgets, and failover.
  • ADR-008 — Tailscale for remote access. Zero port-forwarding, identity-based access, WireGuard-backed encryption; ACL policy lives in the monorepo as code. Now implemented for the core path — the Tailscale Kubernetes Operator is deployed through ArgoCD and LiteLLM is served privately at https://litellm.tail33031c.ts.net, which let the LAN NodePort be removed entirely rather than kept as a parallel door.

Incident-driven ADRs (the debugging stories worth keeping) live in Incidents & Lessons.

Anti-Patterns

  • Mixing AI and media workloads on the same node
  • Treating the media server as an experiment box
  • Adding orchestration layers (Kubernetes, Backstage, Crossplane) before they solve a real problem
  • kubectl apply-ing anything that should have gone through Git