Kubernetes
Layer 2 of Architecture: two Kubernetes clusters under
clusters/, reconciled by FluxCD on every merge tomain. The Terraform bootstrap roots own CoreDNS and Flux itself; Flux owns post-bootstrap cluster state. ArgoCD is installed as a Flux HelmRelease and owns no applications;terraform/argo/wires the provider and declares no resources. Reconciliation mechanics and the apply model live on Architecture/GitOps.Clusters
clusters/folly/β independent, fully capable on-site cluster. Nodes:optiplex(control-plane),riptide,shale(workers).clusters/offsite/β independent, fully capable remote-site cluster. Nodes:retrofit(control-plane),oldschool(worker).clusters/base/β resources shared by both clusters, referenced by relative path from each cluster's manifests.Hardware, serials, and per-host quirks for all five nodes: Fleet.
The workloads each cluster reconciles: Architecture/Cluster Applications.
The base/ sharing pattern
A cluster's
flux-system/kustomization.yamllists../../base/flux-systemas a resource alongside its own per-domain Kustomizations. That pulls in the shared platform layer βarc-system,external-secrets-operator,onepassword-connect,sandbox(agent-sandbox),valkey-operatorβ fromclusters/base/flux-system/.Individual per-cluster manifests also reference specific
base/subtrees directly wherever an app or component is identical across sites:clusters/folly/apps/kustomization.yamllists../../base/apps/iperf3and../../base/apps/reloadernext to its cluster-only app directories;clusters/folly/storage/kustomization.yamllists../../base/storagenext tonfs-provisioner;clusters/folly/networking/cloudflare/kustomization.yamllists../../../base/networking/cloudflarenext to its own tunnel credentials.Where a cluster has no local override at all, its Flux
Kustomizationcustom resource pointsspec.pathstraight at the base directory βclusters/offsite/flux-system/storage.yaml'spathis./clusters/base/storage, since offsite carries no cluster-specific storage overlay.clusters/base/kustomization.yamlitself holds justcluster-runtimeclass.yamlandcluster-settings.yamlβ the shared settings ConfigMap. Per-domain Flux Kustomizations substitute it alongside their site's topology resources and secrets.
Bootstrap
clusters/<site>/bootstrap/is a standalone OpenTofu root (stategs://homelab-ng/clusters/<site>/bootstrap) that calls the sharedterraform/modules/flux-bootstrapmodule. The module deploys CoreDNS before installing theflux-operatorandflux-instanceHelm releases (oci://ghcr.io/controlplaneio-fluxcd/charts) into theflux-systemnamespace. It also cuts an ECDSA deploy key, registers it read-only against theinfraGitHub repository, and writes the key into theflux-github-app-credentialsSecret that Flux uses to pull.node-labels.tfin the same root labels each node:node-role.kubernetes.io/control-planeor/worker, plusbgp-enabled: "true"on every node in both clusters.flux-values.yamlsupplies the Helm values forflux-instance. Itsinstance.syncblock is what makes theFluxInstanceown the root git sync:GitRepositorynameinfra, refrefs/heads/main, pathclusters/<site>/flux-system,pullSecret: flux-github-app-credentials. The operator generates the rootGitRepository/Kustomizationfrom that block β there is no hand-applied root manifest.This root applies through Atlantis like any other Terraform module β see Architecture/GitOps.
Nodes
clusters/folly/nodes/adds the Intel device-plugin operator, the GPU device plugin, and node-feature-discovery (intel-uhd-630.yaml) for the folly nodes' integrated GPUs. offsite has nonodes/directory β no device-plugin layer runs there.
Storage
clusters/base/storage/provideslocal-path-provisioner, shared by both clusters.clusters/folly/storage/layers an NFS provisioner (nfs-provisioner/, backed by spore) and a static PV (spore-pv.yaml) on top. offsite usesbase/storageunmodified.
Monitoring
clusters/base/monitoring/providesmetrics-serverand the whole log path, shared by both clusters:vectorshipskubernetes_logsandjournaldintovictoria-logs, which accepts the Loki push protocol at/insert/loki/api/v1/push. Grafana reads it through thevictoriametrics-logs-datasourceplugin.clusters/folly/monitoring/layers kube-prometheus-stack on top, plus Grafana dashboards and hand-writtenServiceMonitor/EndpointSlicepairs that scrape node-exporter and CoreDNS metrics off hosts Prometheus can't discover via the Kubernetes API (capsule,spore,cloudpi4,radiopi0,forge).clusters/offsite/monitoring/runs its own kube-prometheus-stack and Tempo, keeping metric/log/trace traffic on the local network β only Grafana is exposed (grafana-offsite.lolwtf.ca); Prometheus and Alertmanager stayClusterIP-only.mise run k8s:render-appsrenders both clusters' monitoring trees, andmise run k8s:check-ruleslints everyPrometheusRulethat render produces and runs the alert unit tests written beside them inclusters/base/monitoring/.clusters/base/monitoring-crds/installs the Prometheus Operator CRDs as their ownHelmRelease, ahead ofmonitoringrather than inside it βbase/monitoringholds a rawServiceMonitor, and Flux dry-runs every object in aKustomizationbefore applying any of them, so a cluster without the CRD already present refuses the wholemonitoringKustomization, including the chart that would supply the CRD. offsite'smonitoringKustomizationdependsOn: monitoring-crdsfor that reason. folly has the same latent ordering problem but is not wired tomonitoring-crdsyet: its CRDs predate the split and carry no Helm ownership metadata forprometheus-operator-crdsto adopt β see Runbooks/Adopt Folly Monitoring CRDs.
Networking
Cilium (CNI + BGP load balancing) and the Gateway API live under each cluster's
networking/, built from shared Helm releases inclusters/base/networking/{cert-manager,cloudflare,external-dns,tailscale}plus per-cluster secrets and config. Full detail, including the cross-site firewall gating, is on Architecture/Networking.
Network facts: the cluster-topology SSOT
clusters/<site>/config/cluster-topology.jsonis the Fluxcluster-topologyConfigMap (namespace: flux-system) β applied as-is, because JSON is valid YAML, with no generator step. It's a plain resource inclusters/<site>/config/kustomization.yaml.datais a flatstring β stringmap:CLUSTER_NAME,API_SERVER_IP,API_SERVER_HOSTNAME,API_SERVER_PORT,ROUTER_IP,K8S_NODE_CIDR,CILIUM_POD_CIDR,SERVICE_CIDR,CLUSTER_DNS,CILIUM_NATIVE_ROUTING_CIDR,LB_RANGE,BGP_GATEWAY_ASN,BGP_CILIUM_ASN. It stays flat because Flux'spostBuild.substituteFromonly accepts string values, so a list (CLUSTER_DNS) is comma-separated and numbers (API_SERVER_PORT, both ASNs) are stringified.Every other per-domain Flux
Kustomization(apps,networking,storage,monitoring,arc-runners)dependsOn: configand substitutes${VAR}from this ConfigMap β pluscluster-settingsand thecluster-secretsSecret β viapostBuild.substituteFrom. Folly storage and monitoring also substituteclusters/folly/config/lab-topology.json, the flat-string ConfigMap that owns Lab/future CIDRs and host addresses..github/workflows/topology-contract.ymlrunsconftestagainst both clusters'cluster-topology.jsonon every touching PR, enforcing the schema in.github/policy/cluster-topology.rego: every required key present and non-empty, IPs/CIDRs/ports/ASNs well-formed,API_SERVER_IPandROUTER_IPinsideK8S_NODE_CIDR,LB_RANGEdisjoint fromK8S_NODE_CIDR, and no CIDR overlap between the two clusters' files.Consumers beyond Flux:
nix/services/k8s/networks.nixreads cluster topology withbuiltins.fromJSON;nix/lib/lab.nixprojects lab topology. OpenTofu roots instantiateterraform/modules/cluster-topology, selecting the site and ConfigMap name. Full picture on Architecture/Networking.
Secrets
SOPS-encrypted in-repo (
clusters/**/*.sops.yaml), decrypted per-Kustomization viadecryption.provider: sopsβ see Architecture/Secrets and PKI.
Linked references 9
Networking spans all four layers: UniFi VLANs and BGP at Layer 1/3 (terraform/network/), Cilium and the Gateway API inside each cluster at Layer 2 (clusters/*/networking/), Cloudflare and Tailscale gluing sites together at Layer 3. This page is the single place the whole story lives. Cluster composition is on Architecture/Kubernetes; host hardware is on Fleet; live discovery of the running UniFi controller is the unifi-network skill (Runbooks/Inspect UniFi Network).
The per-cluster cluster-topology ConfigMaps (clusters/<site>/config/cluster-topology.json) are the SSOT for every cluster network fact β full mechanism (Flux substituteFrom, conftest schema check, Nix/Terraform consumers) is on Architecture/Kubernetes. The current values:
Step-by-step: Runbooks/Kubernetes GitOps Change. Cluster internals: Architecture/Kubernetes.
Layer 2 β Kubernetes (Architecture/Kubernetes)
Use this when changing manifests under clusters/ or inspecting Flux deployment state. Architecture lives in Architecture/Kubernetes; the apply model is on Architecture/GitOps.
Roles, cluster membership, and node addresses are not recorded here. Hosts are declared in flake.nix; network facts live in the topology SSOT described on Architecture/Kubernetes. Read those.
Declared in nix/hosts/. See Architecture/Kubernetes for cluster composition.
What actually runs on the two Kubernetes clusters, and where its manifests live. Cluster mechanics are on Architecture/Kubernetes; first-party source and image builds are on Architecture/Applications.
Kubernetes β two fully capable clusters, folly on-site and offsite at the remote site, reconciled by FluxCD. See Architecture/Kubernetes.