Skip to content

GitOps & the app-of-apps

Ultron Infra runs on one idea: Git is the desired state, and a controller continuously makes the cluster match it. You never kubectl apply by hand — you commit YAML, push, and Argo CD reconciles. This is GitOps, and it applies the same way to every app on the platform.

flowchart LR
  Dev([You]) -->|git push| Repo[(gitops repo)]
  Repo -->|polls / webhook| Argo[Argo CD]
  Argo -->|diff desired vs live| K8s[(Kubernetes)]
  K8s -->|actual state| Argo
  Argo -->|apply changes| K8s
  Argo -.->|out of sync? self-heal| K8s

Argo CD compares the manifests in Git against what’s live in the cluster. If they differ, it applies the difference (and with selfHeal, it reverts any manual drift).

A single root Application points at the apps/ directory. Every file in apps/ is itself an Argo CD Application that installs one component. So you bootstrap the entire platform by applying one manifest; everything else cascades.

flowchart TD
  Root[root Application] --> A[apps/cnpg-operator]
  Root --> B[apps/keycloak-operator]
  Root --> C["apps/&lt;app&gt;<br/>(e.g. penvoice)"]
  Root --> D[apps/keycloak-test]
  A --> A1[CloudNativePG operator]
  C --> C1[API Rollout + Postgres + ingress]
  D --> D1[Keycloak instance + Postgres]

Shared infrastructure (operators, the auth server) lives alongside per-app entries. Each onboarded app — Penvoice today, with Ayde, Talon, and Sonke coming — is just one more apps/<app> file.

Why it matters: adding a new app is just a new file in apps/ + a folder in workloads/. Rebuilding from scratch is “install Argo CD → apply the root app → everything reconciles.” See Onboard an app for the recipe.