* feat: introduce partition/region/ceph-cluster model across the stack
Formalize partition -> region -> {one k8s cluster, many ceph clusters} and
thread it through every layer plus a new yuctl ops CLI.
- tf: deployment/<partition>/<region>/<stack> layout; terragrunt path-parse +
state key yucca/<partition>/<region>/<stack>; per-region region.hcl (role,
site_id, datacenter, provider_code, domain); env->partition / site->region
renames (NetBird object names byte-identical); standardized per-stack
`discovery` output contract (secrets as op:// refs).
- k8s: clusters/<partition>/<region>/ (staging/austin, prod/htz-fsn1, dev/local);
role-based kustomize components (primary/secondary); hybrid cluster-settings
(TF-rendered identity + human fragment); dev-mirror folded into dev/local;
charts regrouped into charts/{apps,platform,lib,dev}.
- ci: infra.yml partition/region discovery matrix; partition-keyed path filters;
<partition>-<region> environment gates; image-versions path moves.
- ansible: inventories under <partition>-<region>/<cluster>.
- yuctl: Go/cobra CLI reading the discovery contract from TF state.
- Retire the sietch-talos libvirt VM cluster (dev@local is the k3d cluster);
ceph inventory_dirname -> <partition>-<region>/<cluster>.
Verified: mise k8s:validate green (3 clusters); yuctl go build/vet; tofu
validate pre-merge (all 9 stacks). Live-staging state migration NOT run.
* fix typo
* commit
Bootstrap (fresh cluster → Flux-managed)
One-time procedure to take an empty cluster to a self-reconciling Flux install of this tree. Everything after step 3 is GitOps — no further kubectl needed.
1. Install Flux
flux check --pre
flux install # or the flux-operator, once we adopt it
2. Git auth (the repo is private)
kubernetes/apps/dev/local/repos/git-yucca.yaml references secretRef: yucca-repo-auth.
Create it before applying anything, using a read-only deploy key (preferred)
or a fine-grained PAT:
# deploy key (read-only) — add the printed public key to the GitHub repo
flux create secret git yucca-repo-auth \
--url=ssh://git@github.com/immich-app/yucca \
--ssh-key-algorithm=ecdsa --ssh-ecdsa-curve=p521
# …or a fine-grained PAT over https
flux create secret git yucca-repo-auth \
--url=https://github.com/immich-app/yucca \
--username=git --password="$GITHUB_TOKEN"
If you use the ssh deploy key, switch spec.url in git-yucca.yaml to the
ssh://git@github.com/... form.
3. Seed the sources + entrypoint from the local checkout
The cluster-repos/cluster-apps Kustomizations pull from the yucca
GitRepository — which doesn't exist until something applies it. Break the
chicken-and-egg from your checkout:
kubectl apply -k kubernetes/apps/dev/local/repos # GitRepository + HelmRepositories
kubectl apply -k kubernetes/clusters/dev/local # cluster-repos -> cluster-apps
From here Flux owns the tree: it reconciles kubernetes/apps/dev/local/repos
(including any future source changes) and kubernetes/apps/dev/local.
4. Watch it converge
flux get kustomizations --watch
flux get helmreleases -A
Expected order: operators (cnpg-operator, rook-ceph-operator) →
rook-ceph-cluster → yucca-database/yucca-object-user → apps → yucca-web.
Before pointing this at a real prod cluster: the app HelmReleases still carry dev-mirror values and
TODO(prod)markers (image registries/tags, OIDC endpoints, ingress, persistence, ExternalSecrets).mock-oidcand the Rook dev cluster are dev-only. Seekubernetes/README.md.