Repository Layout
The repo has three top-level directories that carry all Kubernetes manifests. A fourth, tofu/, holds OpenTofu modules for cloud provisioning. Task automation lives in mise.toml at the root.
Directory tree
Section titled “Directory tree”Directoryclusters/ Flux CD entrypoint — one Kustomization file per site per environment
Directorylocal/ manifests for the local physical cluster
Directoryflux-system/
- …
- infrastructure.yaml points at
infrastructure/overlays/local/ - dev-ratch.yaml
- dev-sunrice.yaml
- staging-local-ratch.yaml
- …
Directoryremote/ manifests for the remote (cloud) cluster
Directoryflux-system/
- …
- infrastructure.yaml points at
infrastructure/overlays/remote/ - staging-remote-ratch.yaml
- prod-ratch.yaml
- prod-sunrice.yaml
- …
Directoryinfrastructure/ shared cluster services (Traefik, cert-manager, monitoring)
Directorybase/ cluster-agnostic service manifests
Directorymonitoring/
- …
Directorytraefik/
- …
Directorycert-manager/
- …
Directoryoverlays/ cluster-specific patches
Directorylocal/
- …
Directoryremote/
- …
Directoryapps/ application workloads — Deployments, Services, Ingresses
Directorybase/ environment-agnostic manifests, one directory per site
Directoryratch/
- …
Directorysunrice/
- …
Directorynational-intermodal/
- …
- …
Directoryoverlays/ environment-specific patches (namespace, replicas, image tags)
Directorydev/
Directoryratch/
- …
Directorysunrice/
- …
- …
Directorystaging-local/
- …
Directorystaging-remote/
- …
Directoryprod/
- …
Directorytofu/ OpenTofu modules for cloud provisioning
Directoryremote/
Directorydigitalocean/
- …
Directorygoogle/
- …
- shared.tfvars shared variables across providers
Directory.mise/ CLI/TUI tool and build scripts
- …
Directorydocs/ this documentation site
- …
- mise.toml task runner config — installs tools, defines automation tasks
- secrets.yaml SOPS-encrypted secrets (Age key in cluster)
clusters/: one file per site per environment
Section titled “clusters/: one file per site per environment”Each file in clusters/<cluster>/ is a Flux Kustomization that tells Flux where to find a site’s overlay:
apiVersion: kustomize.toolkit.fluxcd.io/v1kind: Kustomizationmetadata: name: dev-ratch namespace: flux-systemspec: interval: 5m sourceRef: kind: GitRepository name: flux-system path: ./apps/overlays/dev/ratch prune: true dependsOn: - name: infrastructureThe dependsOn field ensures infrastructure (Traefik, cert-manager, monitoring) reconciles before any site rolls out.
apps/: base manifests and overlays
Section titled “apps/: base manifests and overlays”apps/base/<site>/ holds the deployment, service, and ingress for a site — cluster-agnostic, environment-agnostic. Each overlay in apps/overlays/<env>/<site>/ references that base and sets the namespace:
apiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationnamespace: dev-ratchresources: - ../../../base/ratchEnvironment-specific patches — replica counts, image tags, resource limits — live alongside as patches.yaml. There is only one level of overlay. An overlay points directly at its base. No stacking overlays on top of overlays.
infrastructure/: shared cluster services
Section titled “infrastructure/: shared cluster services”infrastructure/base/ contains kube-prometheus-stack, Traefik, and cert-manager. Each cluster gets an overlay in infrastructure/overlays/local/ or infrastructure/overlays/remote/ with cluster-specific patches. Each cluster’s infrastructure.yaml in clusters/ points at that overlay, so both clusters share the same base config but can diverge where needed.
Namespace convention
Section titled “Namespace convention”Namespaces follow the pattern <env>-<site> — for example, dev-ratch or prod-sunrice. Two environments share each physical cluster (local hosts dev and staging-local; remote hosts staging-remote and prod), so the prefix prevents collisions. Each namespace maps to exactly one site in one environment, which also satisfies compliance isolation requirements.
Adding a site or environment
Section titled “Adding a site or environment”To add a site, create apps/base/<site>/, one overlay per environment under apps/overlays/<env>/<site>/, and one Flux Kustomization per cluster under clusters/<cluster>/. No existing files change. Because each site has its own Kustomization object, a broken manifest for one site does not block reconciliation for any other.
Adding an environment follows the same logic: create overlays for each site, add Flux Kustomizations in the relevant cluster directory, commit and push.
mise.toml: task automation
Section titled “mise.toml: task automation”mise.toml at the repo root installs all required tools via mise and defines tasks for common operations:
| Task | What it does |
|---|---|
mise run encrypt <file> | Encrypt a file in place with SOPS |
mise run edit <file> | Decrypt and edit a SOPS file (fetches age key from cluster) |
mise run decrypt <file> | Decrypt a SOPS file to stdout |
mise run apply <path> | Apply a Kustomize overlay directly, suspending and resuming Flux |
mise run apply-dev | Apply all dev overlays directly |
mise run flux-bootstrap | Bootstrap Flux onto a cluster |
mise run tui | Start the management TUI |
mise run tofu:do:plan | Preview DigitalOcean infrastructure changes |
mise run tofu:gke:apply | Apply GKE infrastructure |
The apply task suspends all Flux Kustomizations before running kustomize build | kubectl apply, then resumes them on exit — including on Ctrl+C. This prevents Flux from reverting a manual apply mid-operation.
Tools declared in the [tools] section — kubectl, flux2, helm, kustomize, sops, age, skaffold, opentofu, and others — are pinned to latest and installed automatically when entering the directory.