In Flux v2, how do source-controller and kustomize-controller divide the work of getting manifests from a Git repository onto a cluster, and what does each resource own?
answer
- fetching and applying are different jobs
- one CRD per controller
- artifact with a revision, then a build
- sourceRef, path, interval
- one source, many Kustomizations
basics
~10 sFlux splits fetching from applying. source-controller pulls a repository (GitRepository, OCIRepository, HelmRepository) and publishes a verified artifact; kustomize-controller takes that artifact, builds the manifests and applies them to the cluster on its own interval.
solid answer
~50 sFlux v2 is not one agent but a set of controllers, each with its own CRD. `source-controller` owns acquisition: a `GitRepository` (or `OCIRepository`, `HelmRepository`, `Bucket`) says where to fetch from, which ref, how often, and with which credentials. When it succeeds it publishes an immutable artifact — a tarball of the fetched tree, with a revision and a checksum — and records it in the resource's status. `kustomize-controller` owns application: a `Kustomization` points at a source with `sourceRef`, picks a `path` inside the artifact, builds it with kustomize, and applies the result with server-side apply on its own `interval`. `helm-controller` does the same job for a `HelmRelease`. The payoff is composition: one source can feed many Kustomizations with different paths, intervals, service accounts and prune settings, and a fetch failure is visibly a different failure from a build or apply failure.
code
yaml · 25 linesapiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: fleet
namespace: flux-system
spec:
interval: 1m
url: ssh://[email protected]/example/fleet
ref:
branch: main
secretRef:
name: flux-system
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 10m
path: ./apps/production
prune: true
sourceRef:
kind: GitRepository
name: fleetgo deeper
Be able to name the two resources in the common pair — a GitRepository saying where the manifests come from, and a Kustomization saying which path to apply and how often.
Explain the artifact handoff: source-controller fetches and publishes a revision, kustomize-controller builds that path and applies it on its own interval. Name helm-controller and HelmRelease as the parallel path for charts.
Show how the split changes operations — reusing one source across many Kustomizations, per-tenant serviceAccountName on the apply, suspending one path during an incident, and reading which half of the pipeline is red before opening logs.
Own the trade: composable primitives give isolation and per-tenant identity but no single object that represents an application, so you must invest in conventions and observability to make the fleet legible to on-call engineers.
## Two jobs, deliberately separated A GitOps agent has to do two very different things: get the desired state from somewhere trustworthy, and make the cluster match it. Flux v2 (the GitOps Toolkit) refuses to merge those into one object. Instead it ships a set of Kubernetes controllers, each owning a small set of custom resources, and you compose them. The two you meet first are `source-controller` and `kustomize-controller`. ## source-controller: acquisition `source-controller` reconciles source resources. The common ones are: - `GitRepository` — a Git URL plus a ref (`branch`, `tag`, `semver`, `commit` or `name`), an `interval`, and a `secretRef` naming the Secret with SSH keys or a token. - `OCIRepository` — the same idea against an OCI registry artifact, which is how many teams ship manifests without cloning Git into the cluster. - `HelmRepository` and `Bucket` — Helm chart indexes and object-storage buckets. On each interval it fetches, optionally verifies a signature, and produces an **artifact**: a tarball of the tree, served over HTTP inside the cluster by source-controller itself. The resource's `status.artifact` carries the revision (as of Flux 2.x formatted like `main@sha1:<commit>`), a digest, and the URL. The `Ready` condition tells you whether the last fetch succeeded. Nothing here touches your workloads. source-controller has no opinion about what the files mean. ```yaml apiVersion: source.toolkit.fluxcd.io/v1 kind: GitRepository metadata: name: podinfo namespace: flux-system spec: interval: 1m url: https://github.com/stefanprodan/podinfo ref: branch: master ``` ## kustomize-controller: application A `Kustomization` is the consumer. It names a source with `sourceRef`, a `path` inside the artifact, its own `interval`, and how to apply: `prune`, `targetNamespace`, `patches`, `postBuild` variable substitution, `serviceAccountName` for the identity the apply runs as, `wait`/`healthChecks`, `dependsOn`, `suspend`. On each interval — and again whenever the source publishes a new revision — kustomize-controller pulls the artifact, runs a kustomize build over `path` (generating a `kustomization.yaml` if the directory has none), and applies the result with **server-side apply** under its own field manager. It records an inventory of everything it applied so it can later prune what disappears. Its `Ready` condition reports the outcome, with reasons such as `ReconciliationSucceeded` or `HealthCheckFailed`. Note the name collision that trips people up: this `Kustomization` is a Flux CRD in `kustomize.toolkit.fluxcd.io`, and it is a different thing from the `kustomization.yaml` file that the kustomize tool reads. The Flux resource is the schedule and the policy; the file is the build input. ## helm-controller and the rest `helm-controller` reconciles `HelmRelease`, which references a chart from a source and performs install/upgrade/rollback. `notification-controller` reconciles `Alert`, `Provider` and `Receiver` for outbound notifications and inbound webhooks. Image scanning and write-back live in their own controllers again. Every one of them is an ordinary Kubernetes controller watching its own CRDs — no central brain, no shared database. ## Why the decomposition pays - **Reuse.** One `GitRepository` for the whole cluster, then a dozen `Kustomization` objects pointing at different `path`s in it. You fetch once, apply many. - **Independent tuning.** Poll Git every minute; re-apply an expensive path every ten. Suspend one Kustomization during an incident without freezing the rest. - **Legible failure.** A red source means "I could not fetch" — wrong credentials, bad ref, unreachable host. A red Kustomization means "I fetched but could not build or apply". You know which half to debug before you read a single log line. - **Per-tenant identity.** Because applying is its own object, each `Kustomization` can carry `serviceAccountName`, so a tenant's manifests are applied with that tenant's RBAC rather than with cluster-admin. - **Ordering.** `dependsOn` between Kustomization objects only makes sense because applying is a first-class resource. ## The cost There is no single object that shows you "my app". The state of one application is spread over a source, one or more Kustomizations and possibly a HelmRelease, and you assemble the picture with `flux get sources git`, `flux get kustomizations` and `flux tree`. That is the trade Flux makes: composable primitives instead of one aggregate.
- If the GitRepository stops fetching successfully, what does the Kustomization do?It keeps reconciling against the last good artifact, so the cluster stays at the last successfully fetched revision rather than drifting or being torn down. The GitRepository's `Ready` condition goes False with the fetch error, while the Kustomization can still report success — which is exactly why you check the source first when a new commit fails to appear.
- Why does kustomize-controller use server-side apply rather than a client-side apply?Server-side apply gives Flux a named field manager, so the API server tracks which fields Flux owns. Drift on Flux-owned fields is corrected on the next reconcile, conflicts with another controller surface as explicit errors instead of silent last-writer-wins, and fields Flux never sets are left alone.
- What is the difference between the Flux Kustomization resource and a kustomization.yaml file?The file is kustomize build input — bases, patches, generators — and lives in the repository. The Flux `Kustomization` is a cluster resource that says which source and path to build, how often, with what identity, whether to prune, and what to wait for. Flux generates a kustomization.yaml on the fly if the path has none.
saying these in an interview costs you the question
- Thinks Flux is one agent that clones and applies in a single loop
- Confuses the Flux Kustomization CRD with the kustomization.yaml file
- Believes each application needs its own GitRepository
- Says Flux runs kubectl apply as cluster-admin for everything
- Assumes a fetch failure immediately deletes or reverts workloads