# ctrl/addons.sh and ctrl/addons/*.sh ## addons.sh Each addon is its own idempotent script in `ctrl/addons/` — adding one is adding a file, not editing a dispatcher. ## airflow.sh Airflow needs a metadata database before it will start at all, so the script refuses rather than rolls a pod that will CrashLoopBackOff while the real problem (postgres missing from `ADDONS`) stays invisible in the logs. One pod on `standalone`, matching the compose cabinet: migration, admin user, scheduler and webserver in a single container. The official chart's five deployments model an installation; switching this on means wanting pipelines. ## cert-manager.sh In a regulated estate almost everything is TLS, so the interesting question during onboarding is "does this service present a cert my client trusts" — not "can I reach a public ACME server". A local CA answers that offline, which is also what makes the air-gapped profile usable. ## metallb.sh — why it matters Real manifests use LoadBalancer, because a real cluster has one. On a bare kind cluster those Services sit at `EXTERNAL-IP ` forever with no error anywhere — the deployment looks fine and simply is not reachable. Without MetalLB, every such Service has to be edited to NodePort, which means the local manifests stop matching the ones being modelled. The address pool is derived from the kind Docker network at install time, not hardcoded: Docker picks that subnet, it differs between machines, and a pool outside it is silently unroutable. ## metallb.sh — waiting for the controller `kubectl wait` on a selector errors out immediately when nothing matches yet, and right after apply the ReplicaSet has not created the pod — so it loses a race it looks like it should win. `rollout status` waits for the Deployment itself and handles the not-yet-created case. ## metrics-server.sh kind nodes serve kubelet metrics over a self-signed cert, so the standard manifest never becomes ready without `--kubelet-insecure-tls`. That is fine here (it is a local cluster) and is the single most common reason metrics-server sits at 0/1 on kind. ## postgres.sh — cabinets A cabinet is a public service dropped into the environment as-is — the upstream image, unmodified, reachable at a known address. `postgres.sh` is the cluster half of it; the compose half is a `service.yml` beside a `cabinet.json`. The declaration is made once and both paths read it, so nothing is remembered twice. ## postgres.sh — plain manifests, one replica Plain manifests rather than a helm chart, matching the other addons: a chart repo is a network dependency, and the offline example profile exists precisely so there is a path with none. The image is pinned in `ctrl/versions.env` and can be preloaded into a local registry like every other image here. One replica on a PVC. This models a dependency for local work, not a highly-available database, and pretending otherwise on a kind node would be a more elaborate lie rather than a more useful one. ## redis.sh Cache, and the broker anything queue-shaped runs on. No persistence: a broker that loses its queue on restart is the honest local model, and a PVC here buys nothing but a volume to clean up.