Skip to content

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.

  • 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:

clusters/local/dev-ratch.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: dev-ratch
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: flux-system
path: ./apps/overlays/dev/ratch
prune: true
dependsOn:
- name: infrastructure

The dependsOn field ensures infrastructure (Traefik, cert-manager, monitoring) reconciles before any site rolls out.

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:

apps/overlays/dev/ratch/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev-ratch
resources:
- ../../../base/ratch

Environment-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/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.

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.

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 at the repo root installs all required tools via mise and defines tasks for common operations:

TaskWhat 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-devApply all dev overlays directly
mise run flux-bootstrapBootstrap Flux onto a cluster
mise run tuiStart the management TUI
mise run tofu:do:planPreview DigitalOcean infrastructure changes
mise run tofu:gke:applyApply 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.