HomeLab GitOps repository. Reconciled by flux.
| .docs | ||
| apps | ||
| clusters/phonk | ||
| infrastructure | ||
| .gitignore | ||
| README.md | ||
| TODO.md | ||
Gigawave
GitOps repo for the phonk Talos Linux homelab cluster (3 nodes), managed by Flux v2.
Cluster Overview
- OS: Talos Linux
- Hardware
- HP ProDesk 400 G5 (4c, 48GB, 500GB SATA SSD, 2TB NVME)
- HP ProDesk 400 G5 (4c, 48GB, 500GB SATA SSD, 2TB NVME)
- HP ProDesk 400 G5 (4c, 64GB, 500GB SATA SSD, 2TB NVME)
- Network: Local 2.5GbE
- CNI
- Cilium
- kube-proxy replacement
- Hubble (tbh i dont see much value here... thinking of turning it off)
- Cilium
- Storage
- Local Path — workloads with PVCs are pinned to a node for rescheduling
- Secrets
- OpenBao
- PostgreSQL backend
- Unseal via static seal secret applied before flux bootstrap
- External Secrets Operator
- OpenBao
- Gateway
- Envoy
- external
1bleim.deanuth.devfullbyte.de
- internal
ops.phonk.jukebox.fullbyte.dehome.anuth.dev
- external
- Envoy
GitOps
- Bootstrap from
github.com/boneskewer69/gigawave - Switch to Self-hosted Forgejo at
forge.fullbyte.de/ - Updates: Renovate bot only (no Flux image automation)
Docs
- Bootstrap — Flux install, manual secrets, SSH deploy key, reconcile reference
- OpenBao — init, zone KV + auth setup, OIDC, initial secrets
- Talos / BGP — node operations, MikroTik BGP config
- Each app has its own README detailing why it exists and what should be deployed here
Repository Structure
gigawave/
├── clusters/phonk/ # Flux Kustomization CRs
│ ├── flux-system/ # flux-operator FluxInstance
│ ├── infrastructure/ # one .yaml per infra layer
│ └── apps/ # one .yaml per zone
├── infrastructure/
│ ├── namespaces/ # zone + operator namespaces
│ ├── sources/ # HelmRepository + GitRepository CRs
│ ├── cni/cilium/ # Cilium HelmRelease (kube-system)
│ ├── csi/local-path/ # local-path-provisioner HelmRelease
│ ├── gateway/envoy/ # stub — Envoy Gateway migration (future)
│ └── operators/
│ ├── cert/ # cert-manager, cert-manager-webhook-hetzner
│ ├── kafka/strimzi/
│ ├── opensearch/opensearch-operator/
│ ├── postgresql/cnpg/
│ ├── redis/redis-operator/
│ ├── secrets/external-secrets/
│ └── storage/garage-operator/
└── apps/
├── ops/ # namespace: ops — cluster services
│ ├── bao/ # OpenBao HelmRelease + bao-db CNPG cluster
│ ├── config/ # SecretStore, ClusterIssuer, wildcard certs
│ ├── gitops/flux-operator/
│ ├── monitoring/ # kube-prometheus-stack + Thanos
│ ├── network/dns/zone/# CoreDNS for *.ops.phonk.jukebox.fullbyte.de
│ └── pgadmin/
├── fullbyte/ # namespace: fullbyte — fullbyte.de services
│ ├── config/ # SecretStore
│ ├── ci/ # Woodpecker CI
│ ├── forge/ # Forgejo + forgejo-db
│ ├── httpbin/
│ ├── id/ # PocketID + pocketid-db
│ └── s3/ # GarageCluster
├── home/ # namespace: home — home.anuth.dev services
│ ├── config/ # SecretStore
│ └── network/dns/
│ ├── zone/ # CoreDNS for *.home.anuth.dev
│ └── resolver/ # Blocky (upstreams: ops zone + home zone)
├── 1bleim/ # stub (future: 1bleim.de)
├── anuth/ # stub (future: anuth.dev)
└── vanknel/ # stub (future: vanknel.de)
Dependency Graph
graph TD
namespaces[infrastructure-namespaces]
sources[infrastructure-sources]
csi[infrastructure-csi]
crds_gw[infrastructure-crds-gateway-api]
crds_prom[infrastructure-crds-prometheus]
cni[infrastructure-cni]
gateway[infrastructure-gateway]
operators[infrastructure-operators]
ops[apps-ops]
fullbyte[apps-fullbyte]
home[apps-home]
stubs["apps-1bleim / apps-anuth / apps-vanknel"]
namespaces --> sources
namespaces --> csi
sources --> crds_gw
sources --> crds_prom
crds_gw --> cni
crds_gw --> gateway
cni --> operators
operators --> ops
crds_prom --> ops
ops --> fullbyte
ops --> home
ops --> stubs
Key Conventions
- New namespace → add to
infrastructure/namespaces/namespaces.yaml - New Helm repo → add to
infrastructure/sources/helm-repos.yaml - Operator HelmRelease → goes in
infrastructure/operators/<category>/<name>/helmrelease.yaml - Operator instances: one cluster per tenant (e.g.
bao-db,grafana-db,<app>-db). Always useinstances: 3withenablePodAntiAffinity: requiredandtopologyKey: kubernetes.io/hostname— one replica per node, survives two node failures. NonodeSelectorneeded. - pgAdmin: add server to pgAdmin manually after deploy
- Secrets pattern: ExternalSecret (pulls from OpenBao) → Kubernetes Secret → consumed by HelmRelease
valuesFrom - SecretStore: one namespace-scoped
SecretStorenamedopenbaoper zone (apps/<zone>/config/secretstore.yaml), pointing tobao.ops.svc.cluster.local. UseClusterSecretStoreonly for cross-namespace infra (e.g. cert-manager-webhook-hetzner). - Stages: most workloads prod-only. If dev is required, create namespace
<name>-devand deploy a separate instance. - Variable substitution: cluster-wide vars in
cluster-configConfigMap; FluxpostBuild.substituteFrominjects them. Reference as${VAR_NAME}in manifests. - Deployment mode: single instance where possible. Stateful workloads: pin to node, deploy ≥2 if HA is possible, otherwise add backup TODO.
- Storage: prefer
emptyDirfor ephemeral state; PVCs only for data that must survive pod restarts. - Email: outbound via Brevo (SMTP relay). Verified senders:
id@fullbyte.de(PocketID),git@fullbyte.de(Forgejo),noreply@fullbyte.de(all others). Ask before using any other address. OpenBao secret path:secret/mail/de-fullbyte-<app>with keyssmtp-server,smtp-port,smtp-login,smtp-key.
Tooling Ideas
- flux-template — render template from local HelmRelease file
- flux dependson diagram generator
- pgadmin operator
Learnings
- routing
- less is more
- bridge is like a partition for network ports on a switch/router
- add DNS and NTP server to your network early
- wait for NTP to fully sync before relying on time-sensitive services