Git, CI/CD & GitOpsLXXVIII · FluxArchitecture
The Flux architecture — the toolkit composition and the controller model
What you'll learn
- Name the controllers that compose Flux and the CRD each one owns
- Trace the path from a Git commit through source, kustomize, and helm controllers to a Kubernetes reconcile
- Distinguish the controller-per-CRD model from the monolithic-controller model Argo CD uses
- Identify the install boundary (flux CLI bootstrap) versus the runtime boundary (controllers)
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
Flux is not a single binary. It is a composition of independent controllers built from the GitOps Toolkit, each owning a custom resource definition and a reconcile loop. The boundary between controllers is the boundary between CRDs.
The toolkit composition
The GitOps Toolkit ships five controllers, each with one concern:
- source-controller owns
GitRepository,Bucket,OCIRepository,HelmRepository. Reads sources, produces artifacts. - kustomize-controller owns
Kustomization. Consumes a source artifact, runs Kustomize, applies manifests. - helm-controller owns
HelmRelease. Consumes a chart source, reconciles Helm values. - image-automation-controller owns
ImageRepository,ImagePolicy,ImageUpdateAutomation. Scans registries, writes back to Git. - notification-controller owns
Alert,Provider,Receiver. Fans events to Slack, Teams, or webhook.
flowchart LR
G["Git / OCI / Helm source"] --> SC["source-controller"]
SC --> KC["kustomize-controller"]
SC --> HC["helm-controller"]
SC --> IA["image-automation-controller"]
KC --> K8s["Target Kubernetes cluster"]
HC --> K8s
IA -->|commits back| G
KC --> EV["events"]
HC --> EV
SC --> EV
NC["notification-controller"] --> EV
EV --> Slack["Slack / Teams / Webhook"]
The composition is additive: flux install ships source,
kustomize, helm, notification, and image-reflector by default;
image-automation-controller is opt-in. A team that does not use
Helm can skip helm-controller without losing source or kustomize.
The controller-per-CRD model
Each controller watches its own CRDs, fetches the inputs they declare, produces the outputs they promise:
- source-controller watches
GitRepositoryand produces aSourceartifact - a tarball inside the cluster at an in-cluster URL. - kustomize-controller watches
Kustomizationand produces applied Kubernetes resources. - helm-controller watches
HelmReleaseand produces a Helm release in the cluster.
Controllers do not call each other directly. They communicate
through status conditions on CRDs: a Kustomization whose
sourceRef is a GitRepository waits for that source to
report Ready=True before reconciling.
sequenceDiagram
participant Op as Operator
participant SC as source-controller
participant KC as kustomize-controller
participant K8s as Cluster
Op->>SC: flux create source git apps
SC->>SC: clone repo, build artifact
SC-->>Op: status: Ready=True
Op->>KC: flux create kustomization apps
KC->>SC: read artifact (in-cluster URL)
SC-->>KC: artifact bytes
KC->>KC: run kustomize build
KC->>K8s: server-side apply
K8s-->>KC: apply result
KC-->>Op: status: Ready=True
This decoupling is what lets each controller be scaled, restarted, and debugged independently.
Install boundary versus runtime boundary
A Flux install has two boundaries:
- Install boundary.
flux bootstrap(or the GitHub, GitLab, Bitbucket variants) installs the toolkit controllers into the cluster and commits a cluster-side manifest set to the repo underclusters/. One-time per cluster per environment. - Runtime boundary. The toolkit controllers in
flux-system, watching CRDs, reconciling continuously.
The CLI is a client of the controllers, not part of the
runtime. flux get all reads CRDs from the Kubernetes API;
flux reconcile writes an annotation that forces the next
reconcile out of cycle.
Under the hood
The controller-per-CRD decomposition is what makes Flux extensible. New CRDs can be added without modifying existing controllers; existing controllers can be upgraded without touching a team’s CRDs.
Production discipline
- Controllers in
flux-system, scaled independently. Heavy source fan-out needs more source-controller replicas; heavy Kustomize fan-out needs more kustomize-controller replicas. - The toolkit is pinned to a Flux version.
flux installis idempotent but versioned; pin the version in the bootstrap manifest. - Runtime CRDs in
flux-system; application CRDs in their own namespace.
Cross-course references
- Kubernetes for Production Sysadmins - Parts IX-XII (RBAC and CRDs) cover the operator pattern the toolkit implements.
- Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXIV (GitOps Controllers) is the architectural context; Part LXXVII (Argo CD) is the alternative architecture this part contrasts against.
Quiz
Knowledge check · 4 questions
Q1. Which statement best describes how Flux controllers communicate with each other during a reconcile?
Q2. The `flux` CLI is a side-channel client that communicates directly with each Flux controller over a private API.
Q3. Name three of the five core controllers Flux ships and identify the CRD each one owns.
Q4. Diagnose why a Helm release appears stuck and identify which controller in the chain is the bottleneck.
A team runs `flux get helmreleases` after deploying a new HelmRelease called payments-api. The output shows status: Ready=False, reason: DependencyNotReady, and the referenced HelmRepository shows status: Ready=True. The team restarts helm-controller; the condition does not change. The team restarts source-controller; the condition resolves within 60 seconds.
Passing score: 75%. Answers are checked in this browser.