- YAML 66.1%
- MARKDOWN 26.5%
- SHELL 2.1%
- HTML 1.8%
- JavaScript 1.2%
- Other 2.1%
|
All checks were successful
e2e / Validate Renovate config (push) Successful in 41s
e2e / Flux-local render (push) Successful in 34s
e2e / Lint & static validation (push) Successful in 44s
e2e / Kyverno Chainsaw (KinD) (push) Successful in 8s
[Workflow] On Documentation Change / techdocs (push) Successful in 41s
[Workflow] On Documentation Change / Generate (push) Successful in 0s
[Workflow] On Documentation Change / Publish to Garage S3 (push) Successful in 9s
[Workflow] On Documentation Change / Deploy (Backstage / Garage S3) (push) Successful in 0s
[Workflow] On Documentation Change / Build (Zensical) + sync to Garage web (push) Successful in 41s
[Workflow] On Documentation Change / Deploy (docs site / Garage web) (push) Successful in 0s
My previous commit concluded worker-2 could not take the extension without a version bump. Wrong: a concurrent fix landed it by applying the node's machine config before the upgrade (its live install.image still pointed at the base schematic). All three workers now report kata-containers 3.32.0. Verified live: extensions on .32 + runtime.webgrip.io/kata=true on all three nodes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .claude | ||
| .forgejo/workflows | ||
| .github | ||
| .vscode | ||
| bootstrap | ||
| catalog | ||
| docs/techdocs | ||
| kubernetes | ||
| scripts | ||
| talos | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .gitleaks.toml | ||
| .lefthook.toml | ||
| .lefthookrc | ||
| .lycheeignore | ||
| .mcp.json | ||
| .mise.toml | ||
| .renovaterc.json5 | ||
| .shellcheckrc | ||
| .sops.yaml | ||
| .worktreeinclude | ||
| .yamlfmt.yaml | ||
| AGENTS.md | ||
| BLOGPOST-dark-devsecfinops.devto.md | ||
| BLOGPOST-dark-devsecfinops.draft-v1.md | ||
| BLOGPOST-dark-devsecfinops.hn.md | ||
| BLOGPOST-dark-devsecfinops.linkedin.md | ||
| BLOGPOST-dark-devsecfinops.md | ||
| BLOGPOST-dark-devsecfinops.plan.md | ||
| BLOGPOST-dark-devsecfinops.reddit.md | ||
| BLOGPOST-dark-devsecfinops.research.md | ||
| BLOGPOST-dark-devsecfinops.review.md | ||
| BLOGPOST-dark-factory.devto.md | ||
| BLOGPOST-dark-factory.draft-v1.md | ||
| BLOGPOST-dark-factory.hn.md | ||
| BLOGPOST-dark-factory.linkedin.md | ||
| BLOGPOST-dark-factory.md | ||
| BLOGPOST-dark-factory.reddit.md | ||
| BLOGPOST-dark-factory.research.md | ||
| BLOGPOST-dark-factory.review.md | ||
| catalog-info.yaml | ||
| CLAUDE.md | ||
| feat-ci-fast-lane-remaining.patch | ||
| HANDOFF-storage-and-node-strategy.md | ||
| justfile | ||
| LICENSE | ||
| README.md | ||
Homelab Operations Repository 
... managed with Flux, Renovate, GitHub Actions, and Talos
💡 Overview
This is the living source of truth for the Talos-powered cluster behind webgrip.dev. Flux owns every namespace under kubernetes/apps, Renovate watches the whole repo for drift, and GitHub Actions runs validation plus flux diffs before anything merges. TechDocs (in docs/techdocs) surface runtime inventory, Talos node state, and runbooks inside Backstage so docs ship with the manifests.
Kubernetes
My cluster runs on three bare-metal Talos controllers (soyo-1..3) that also schedule workloads, plus two dedicated workers — fringe-workstation (10.0.0.30) and worker-1 (10.0.0.31) — that take write-heavy apps off the control-plane disks. worker-1 carries the Longhorn replicas. Everything runs kube-proxy-free via Cilium, with split-DNS gateways and Cloudflare tunnels for ingress. GitOps keeps the manifests authoritative while just/Mise make local development reproducible.
Core Components
- actions-runner-controller: GitHub Actions scale sets for CI bursts.
- cert-manager: ACME certificates for both envoy gateways.
- cilium: eBPF networking, kube-proxy-free dataplane.
- cloudflared + Cloudflare DNS: tunnel and DNS automation for
*.webgrip.dev. - envoy-gateway: Provides
envoy-internal/envoy-externalGateway API classes. - flux: Source, Kustomize, Helm, and notification controllers.
- k8s-gateway: Split DNS responder for internal resolution.
- kyverno: Policy engine for admission control, background scans, policy reports, and namespace guardrails.
- metrics-server, reloader, spegel: telemetry, config reloads, and OCI image cache.
GitOps
Flux watches the kubernetes/apps tree, reconciling each top-level kustomization.yaml it finds. Those Kustomizations in turn apply HelmReleases, Jobs, ConfigMaps, and SOPS secrets. Renovate opens PRs whenever container tags, Helm charts, or Actions workflows drift; GitHub Actions runs linting plus flux diff --cached against the target cluster before a merge. Secrets are committed only as Age-encrypted SOPS files (see kubernetes/components/sops/), so Flux can decrypt them once the controller pulls from this repo.
📦 Featured Workloads
| Category | Namespace(s) | Highlights |
|---|---|---|
| Platform control | flux-system, kube-system |
Flux controllers, notification receiver, Weave GitOps UI, plus Cilium, CoreDNS, metrics-server, Spegel, and Reloader. |
| Networking & ingress | network |
Envoy internal/external gateways, Cloudflare DNS + Tunnel, and k8s-gateway for split-horizon DNS. |
| PKI & security | cert-manager, kyverno, components/sops |
ACME HTTP-01 + DNS-01 issuers for wildcard certs, Kyverno admission/reporting/generate policies, and shared secrets rendered into namespaces through the SOPS component. |
| CI infrastructure | arc-systems |
Actions Runner Controller plus a Docker-in-Docker runner scale set so GitHub repos can burst jobs onto the homelab. |
| Applications | default, freshrss, invoiceninja |
Echo sample service, FreshRSS HelmRelease with Bitnami bootstrap job, and Invoice Ninja 5.12.39 paired with an app-template-managed MariaDB 11.8.5 StatefulSet on Longhorn storage. |
TechDocs tracks all of these via Backstage catalog entries under catalog/, so you can pivot from docs to manifests without leaving the repo.
Directories
📁 kubernetes
├── 📁 apps # Applications managed by Flux
├── 📁 bootstrap # Talos + Helmfile bootstrap resources
└── 📁 flux # Flux controllers and sources
├── 📁 components # Shared components (SOPS, networking)
└── 📁 meta # Repository definitions
📁 docs/techdocs # MkDocs TechDocs (runtime inventory, Talos state)
📁 talos # Generated Talos configs + patches
📁 scripts # Helper scripts + common libraries
Flux Workflow
graph TD
FS>Kustomization: flux-system] --> |Installs| Flux[Flux Controllers + Operator]
Net>Kustomization: network] --> |Publishes| Gateways[Envoy + Cloudflare Tunnel]
Net --> DNS[k8s-gateway + ExternalDNS]
Certs>Kustomization: cert-manager] --> |Issues| TLS[Wildcard Certificates]
Arc>Kustomization: arc-systems] --> |Deploys| Runners[ARC + gha-runner-scale-set]
Apps>Kustomization: freshrss] --> |Consumes| Gateways
Apps --> |Consumes| TLS
Runners --> |Serve| GitHub
Flux --> |Reconciles| Net
Flux --> |Reconciles| Certs
Flux --> |Reconciles| Arc
Flux --> |Reconciles| Apps
🌐 Networking
graph TD
A>Odido Fiber 1Gb/1Gb]
A --> |Genexis ONT bridge| R[Protectli V1410 · OPNsense]
B>WireGuard / Cloudflare Tunnel] --> |Remote access| R
R --> |TL-SG108PE Port 1 uplink| S1[TP-Link TL-SG108PE]
S1 --> |Port 3 → Q-Link| S2[Q-Link Switch]
S1 --> |Port 2 → Zyxel · AP Port 4| W[Zyxel VMG8825-T50 Wi-Fi 5]
S2 --> |Port 7 → soyo-1| K1([soyo-1])
S2 --> |Port 6 → soyo-2| K2([soyo-2])
S2 --> |Port 5 → soyo-3| K3([soyo-3])
S2 --> |Port 2 → NAS| N([NAS])
R --> |WAN · HTTPS 443| HZ([Hetzner garage-fsn1<br/>off-site S3 · Immich · uptime-kuma])
W --> |Port 1 → Hue| H([Philips Hue bridge])
W --> |Port 2 → Home Assistant| P([Raspberry Pi Home Assistant])
W --> |SSID| W1([Main Wi-Fi])
W --> |IoT SSID| W2([IoT devices])
W --> |Guest SSID| W3([Guest access])
🏘️ Flat LAN
| Device | Role | Address | Notes |
|---|---|---|---|
| Protectli V1410 / OPNsense | Router + firewall | 10.0.0.1 |
DHCP scope 10.0.0.50-10.0.0.150, WireGuard termination, split DNS rules. |
| TL-SG108PE | Managed switch | 10.0.0.2 |
Port 1 uplinks to the Protectli WAN handoff, port 2 feeds the Wi-Fi bridge, and port 3 uplinks the Q-Link switch. |
| Zyxel VMG8825-T50 | Wi-Fi bridge/AP | 10.0.0.3 |
Bridge mode so SSIDs land on the same subnet as wired clients. |
Static infrastructure (Talos nodes, Synology, Home Assistant) keeps IPs below .50 and is reserved in OPNsense Terraform so DHCP drift is impossible.
🌎 DNS
Three ExternalDNS deployments keep Cloudflare public records and k8s-gateway entries aligned. envoy-internal routes stay inside the LAN, while envoy-external hostnames are proxied through Cloudflare Tunnel. OPNsense runs split-horizon DNS—every *.webgrip.dev lookup hits the router, which forwards internally to the k8s-gateway LoadBalancer (10.0.0.26) so services stay reachable on-LAN without touching Cloudflare.
🏠 Home DNS
graph TD
Clients -->|Queries| Router[OPNsense split DNS]
Router -->|webgrip.dev| K8sGW[k8s-gateway LB 10.0.0.26]
Router -->|Other domains| WAN[Upstream DNS]
K8sGW -->|Routes hostnames| Envoy[envoy-internal / envoy-external]
Envoy -->|Publishes| Cloudflare
☁️ Cloud Dependencies
| Service | Use | Cost |
|---|---|---|
| Cloudflare | Authoritative DNS, Zero Trust tunnels for *.webgrip.dev |
~$50/yr |
| GitHub | Repo hosting, Actions, container registry | Free |
| Healthchecks.io | Connectivity + job heartbeat monitoring | Free tier |
| Fastmail | Email + identity provider for alerts | ~$56/yr |
🖥️ Hardware
Canonical, always-measured inventory: Infrastructure at a glance — every machine, disk, label and address in one table. The summary below is a snapshot.
| Num | Device | CPU | RAM | OS / Firmware | Function |
|---|---|---|---|---|---|
| 3 | SOYO Mini PC M4 (Twin Lake N150) | Intel N150 (4C/4T) | 12 GB LPDDR5 | Talos Linux v1.13.4 | Control-plane + workloads; one 512 GB SATA SSD each, shared by etcd + OS + Longhorn; Wi-Fi5/BT5 disabled |
| 1 | HP Z230 Tower Workstation | Intel Core i7-4770 (4C/8T, 3.4 GHz) | 16 GB DDR3 (1 slot free) | Talos Linux v1.13.4 | Dedicated worker fringe-workstation (10.0.0.30) for write-heavy apps; 256 GB SATA SSD (OS) + 1 TB HDD |
| 1 | Gigabyte Z87X-D3H desktop | Intel Core i5-4670K (4C/4T, 3.4 GHz) | 24 GB DDR3-1600 | Talos Linux v1.13.4 | Worker worker-1 (10.0.0.31), added 2026-06-19; most RAM in the cluster; 1 TB Samsung SSD 870 — currently the only Longhorn storage node |
| 1 | worker-2 (reclaimed Proxmox box) | Intel Core i7-6700K (4C/8T, 4.0 GHz) | 16 GB (2 slots free) | Talos Linux v1.13.7 | Worker worker-2 (10.0.0.32), rejoined 2026-08-02; 2 TB Samsung 990 EVO Plus NVMe (install + Longhorn) plus 250 GB/1 TB SSD and 1 TB/2 TB HDD unused |
| 1 | Protectli V1410 | Intel i5 | 8 GB | OPNsense | Router/firewall, DHCP 10.0.0.50-150, WireGuard, split DNS for webgrip.dev |
| 1 | TP-Link TL-SG108PE | — | — | Managed firmware | 8-port 1 GbE switch feeding downstream fan-out |
| 1 | Q-Link 1 GbE switch | — | — | Unmanaged | Directly uplinks Talos nodes for east-west traffic |
| 1 | Zyxel VMG8825-T50 | — | — | Bridge/AP firmware | Wi-Fi AP bridging onto the same flat LAN |
| 1 | Hetzner EX-line (Falkenstein, garage-fsn1) |
Intel Core i7-6700 (4C/8T, 3.4 GHz) | 62 GB | Debian 12 | Off-site: Garage S3 (all backups), Immich, uptime-kuma. 2 × 512 GB Samsung NVMe in RAID1 (452 GB usable). Reachable only via Caddy on 443 |
| Decommissioned 2026-08-02. Held Garage S3, Immich and uptime-kuma; all migrated off-site, then the machine was reclaimed as a Talos node | |||||
| 1 | Raspberry Pi 4 (Home Assistant) | Broadcom | 4 GB | Home Assistant OS | Local automations + integrations |
🔢 Cluster & Upstream IPs
| Device / Endpoint | Purpose | Address |
|---|---|---|
| Protectli V1410 / OPNsense | Router, DHCP, split DNS | 10.0.0.1 |
| TP-Link TL-SG108PE | Managed switch | 10.0.0.2 |
| Zyxel VMG8825-T50 | Wi-Fi bridge/AP | 10.0.0.3 |
soyo-1 |
Talos controller / worker | 10.0.0.20 |
soyo-2 |
Talos controller / worker | 10.0.0.21 |
soyo-3 |
Talos controller / worker | 10.0.0.22 |
fringe-workstation |
Talos worker | 10.0.0.30 |
worker-1 |
Talos worker (Longhorn storage) | 10.0.0.31 |
worker-2 |
Talos worker (Longhorn storage) | 10.0.0.32 |
| Kubernetes / Talos API VIP | Control-plane endpoint | 10.0.0.25 |
k8s-gateway LoadBalancer |
Split DNS responder | 10.0.0.26 |
envoy-internal LoadBalancer |
LAN-only ingress | 10.0.0.27 |
envoy-external / Cloudflare tunnel VIP |
Public ingress origin | 10.0.0.28 |
garage-fsn1 (Hetzner, off-site) |
S3 backups, Immich, uptime-kuma | 116.202.53.185 |
🙏 Thanks
Thanks to the Home Operations Discord, onedr0p for the original cluster-template inspiration, bjw-s for the app-template, and every maintainer building Talos, Flux, Renovate, and the CNCF projects that make GitOps homelabs straightforward.