k8s-gitops
Husband-approved homelab deployed with Talos Linux; managed with Flux, Renovate, and GitHub Actions π€
A slightly overengineered homelab 
... managed with Flux, Renovate, and GitHub Actions
Overview
This repository is the single source of truth for my home Kubernetes cluster and the workloads that run on it. Every cluster resource β from the operating system layer down to individual application Helm releases β is declared as code and reconciled automatically.
The stack is intentionally boring and reproducible:
- Talos Linux β Immutable, API-driven OS that runs nothing but Kubernetes.
- Flux β Continuous reconciliation of cluster state against this repository.
- Renovate β Automated dependency updates across the entire cluster.
- GitHub Actions β Validation and automation on every commit.
Disaster recovery is built in. Wipe every disk in the rack. Minutes later, the cluster is back β applications running, persistent data intact, zero manual steps. It picks up exactly where it left off.
Want to build something similar? Start with onedr0p/cluster-template.
Repository Layout
π bootstrap # One-time cluster bootstrap (helmfile + kustomize) π kubernetes # Everything Flux reconciles ββπ apps # Workloads, grouped by namespace ββπ components # Reusable Kustomize components (alerts, kopiur, etc.) ββπ flux # Flux system configuration and source repositories π talos # Talos machine configs and per-node overrides
Cluster
A semi hyper-converged, three-node Kubernetes cluster running on bare-metal MS-A2 workstations. Persistent storage lives inside the cluster via Rook Ceph, with bulk media offloaded to a dedicated TrueNAS box over NFS.
Core components
- actions-runner-controller β Self-hosted GitHub runners for CI/CD workflows.
- cert-manager β Automated SSL certificate management and provisioning.
- cilium β High-performance container networking powered by eBPF.
- cloudflared β Secure tunnel providing Cloudflare-protected access to cluster services.
- envoy-gateway β Modern ingress controller for cluster traffic management.
- external-dns β Automated DNS record synchronization for ingress resources.
- external-secrets β Kubernetes secrets management integrated with 1Password.
- kopiur β Advanced backup and recovery solution for persistent volume claims.
- multus β Multi-homed pod networking for advanced network configurations.
- rook β Cloud-native distributed storage orchestrator for persistent storage.
- spegel β Stateless cluster-local OCI registry mirror for improved performance.
GitOps workflow
Flux watches the kubernetes directory and reconciles the cluster on every commit. The flow is:
- Flux recursively scans kubernetes/apps and reads each top-level
kustomization.yaml. - Those entrypoints typically declare a
Namespaceand one or more FluxKustomizationresources (ks.yaml). - Each Flux
Kustomizationmaterializes aHelmRelease(or raw manifests) for an application. - Flux applies them in dependency order β e.g. nothing in
rook-cephdeploys until its prerequisites are healthy.
graph LR
classDef kustom fill:#43A047,stroke:#2E7D32,stroke-width:3px,color:#fff,font-weight:bold,rx:10,ry:10
classDef helm fill:#1976D2,stroke:#0D47A1,stroke-width:3px,color:#fff,font-weight:bold,rx:10,ry:10
A["π¦ Kustomization<br/>rook-ceph"]:::kustom
B["π¦ Kustomization<br/>rook-ceph-cluster"]:::kustom
C["π― HelmRelease<br/>rook-ceph"]:::helm
D["π― HelmRelease<br/>rook-ceph-cluster"]:::helm
E["π¦ Kustomization<br/>atuin"]:::kustom
F["π― HelmRelease<br/>atuin"]:::helm
A -->|Creates| C
B -->|Creates| D
B -.->|Depends on| A
E -->|Creates| F
E -.->|Depends on| B
Loading
Networking
A multi-tier home network built on UniFi hardware. The UDM Beast handles routing and firewalling between RCN's 5Gbps WAN and the LAN. A 25G aggregation switch forms the backbone β bonded to the NAS at 25G LACP and to each Kubernetes node at 10G LACP β while a 24-port PoE+ switch fans out to wired clients and access points.
graph LR
%% Class Definitions
classDef wan fill:#f87171,stroke:#fff,stroke-width:2px,color:#fff,font-weight:bold;
classDef core fill:#60a5fa,stroke:#fff,stroke-width:2px,color:#fff,font-weight:bold;
classDef agg fill:#34d399,stroke:#fff,stroke-width:2px,color:#fff,font-weight:bold;
classDef switch fill:#a78bfa,stroke:#fff,stroke-width:2px,color:#fff,font-weight:bold;
classDef device fill:#facc15,stroke:#fff,stroke-width:2px,color:#000,font-weight:bold;
classDef vlan fill:#1f2937,stroke:#fff,stroke-width:1px,color:#fff,font-size:12px;
%% Nodes
RCN[π RCN<br/>5Gbps WAN]:::wan
UDM[π¦ UDM Beast]:::core
AGG[π Pro Aggregation XG]:::agg
NAS[πΎ NAS]:::device
K8s[βΈοΈ Kubernetes<br/>3 Nodes]:::device
SW[π Pro XG 24 PoE]:::switch
DEV[π» Devices]:::device
WIFI[πΆ WiFi Clients]:::device
%% Subgraph for VLANs
subgraph VLANs [LAN +vlan]
direction TB
LOCAL[LOCAL<br/>192.168.0.0/24]:::vlan
TRUSTED[TRUSTED*<br/>192.168.1.0/24]:::vlan
SERVERS[SERVERS*<br/>192.168.10.0/24]:::vlan
SERVICES[SERVICES*<br/>192.168.20.0/24]:::vlan
IOT[IOT*<br/>192.168.30.0/24]:::vlan
GUEST[GUEST*<br/>192.168.40.0/24]:::vlan
end
style VLANs fill:#111,stroke:#fff,stroke-width:2px,rx:0,ry:0,padding:20px;
%% Links
SERVERS -.-> RCN
RCN -.->|WAN| UDM
UDM -- 25G --- AGG
UDM -- 25G --- SW
AGG -- 10G LACP --> K8s
AGG -- 25G LACP --> NAS
SW --> DEV
SW --> WIFI
%% Keep SERVERS->RCN as a hidden layout constraint and style bonded links thicker
linkStyle 0 stroke:transparent,stroke-width:0px,color:transparent;
linkStyle 2 stroke-width:4px;
linkStyle 3 stroke-width:4px;
linkStyle 4 stroke-width:2px;
linkStyle 5 stroke-width:4px;
Loading
DNS
Two ExternalDNS instances handle DNS automation:
- Private β Syncs every route to the UDM Beast via the ExternalDNS UniFi webhook.
- Public β Syncs routes on the
externalGateway to Cloudflare.
The result is split-horizon DNS: at home, public hostnames resolve to LAN IPs, so traffic to my own services never leaves the network.
Hardware
| Device | Count | OS Disk | Data Disk | RAM | OS | Purpose |
|---|---|---|---|---|---|---|
| Minisforum MS-A2 | 3 | 1.92TB M.2 | 3.84TB U.2 + 1.92TB M.2 | 96GB | Talos | Kubernetes |
| 45HomeLab HL15 2.0 | 1 | 1.92TB M.2 | 12Γ22TB HDD + 2Γ7.68TB U.2 | 512GB | TrueNAS SCALE | NFS + Backup Storage |
| JetKVM | 3 | - | - | - | - | KVM for Kubernetes |
| UniFi Dream Machine Beast | 1 | - | 2Γ960GB SSD | - | UniFi OS | Router & NVR |
| UniFi Pro XG Aggregation | 1 | - | - | - | UniFi OS | 25G SFP28 Switch |
| UniFi Pro XG 24 PoE | 1 | - | - | - | UniFi OS | 10G PoE+ Switch |
| UniFi Power Distribution Pro | 1 | - | - | - | UniFi OS | PDU |
| APC SMT1500RM2UNC UPS | 1 | - | - | - | - | UPS |
MS-A2 build
Each MS-A2 (AMD Ryzenβ’ 9 9955HX) workstation is equipped with:
- Crucial 96GB Kit (48GBx2) DDR5-5600 SODIMM
- Samsung 1.92TB M.2 22x110mm PM9A3 NVMe PCIe 4.0
- Samsung 3.84TB U.2 PM9A3 NVMe PCIe 4.0
- Google Coral M.2 Accelerator A+E Key
Thanks
A huge thank you to @onedr0p and the Home Operations Discord community for the knowledge, patterns, and support that made this cluster possible. For more inspiration on running apps in a homelab, browse kubesearch.dev.
Stargazers
License
See LICENSE.
more like this
