Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXVIII · FluxArchitecture

The Flux architecture — the toolkit composition and the controller model

Advanced⏱ ~26 mingitflux

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

Not yet marked complete on this device.

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 GitRepository and produces a Source artifact - a tarball inside the cluster at an in-cluster URL.
  • kustomize-controller watches Kustomization and produces applied Kubernetes resources.
  • helm-controller watches HelmRelease and 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 under clusters/. 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

  1. Controllers in flux-system, scaled independently. Heavy source fan-out needs more source-controller replicas; heavy Kustomize fan-out needs more kustomize-controller replicas.
  2. The toolkit is pinned to a Flux version. flux install is idempotent but versioned; pin the version in the bootstrap manifest.
  3. 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

  1. Q1. Which statement best describes how Flux controllers communicate with each other during a reconcile?

  2. Q2. The `flux` CLI is a side-channel client that communicates directly with each Flux controller over a private API.

  3. Q3. Name three of the five core controllers Flux ships and identify the CRD each one owns.

  4. 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.