The exam names both Argo and Flux, and the lab installs both against the same Gitea for a reason: you should be able to express the same delivery in either. Flux has no Application object. It decomposes GitOps into small CRDs that reference each other, and reading those references is the skill.

needsmake core

Orientation

competency 2.1 · the other engine

Argo CD is one controller with one big CRD. Flux is a toolkit of small controllers with small CRDs, composed by reference. Neither is better; they fail differently, and the exam may hand you either. What transfers is the mental translation, which is the last exercise below.

ControllerOwnsReads
source-controllerGitRepository, OCIRepository, HelmRepository, HelmChart, Bucketfetches and publishes an artifact (a tarball + revision)
kustomize-controllerKustomizationan artifact + a path → builds and applies
helm-controllerHelmReleasea chart source → installs/upgrades a release
notification-controllerAlert, Provider, Receiverevents out (Slack, webhooks) and events in (git push → instant reconcile)
image-*-controllerImageRepository, ImagePolicy, ImageUpdateAutomationregistry tags → commits back to git

That last row is worth knowing exists: Flux can close the CI→CD loop by writing the new image tag into git itself, which is the pull-based answer to "how does a new build get deployed without CI touching the cluster".

Working model: sources and appliers

one artifact, many consumers
GitRepository/platform  (interval 1m, ref: main)
        │ produces artifact @ revision main/9f2c1ab
        ├────────────▶ Kustomization/apps     path ./apps      prune ✓  interval 5m
        ├────────────▶ Kustomization/infra    path ./infra     prune ✓  dependsOn: []
        └────────────▶ Kustomization/tenants  path ./tenants   dependsOn: [infra]

HelmRepository/podinfo ──▶ HelmRelease/podinfo ──▶ release in target namespace

The two-step split is the design: one GitRepository can feed many Kustomizations, each watching a different path with its own interval, health checks and prune setting. dependsOn then orders them, which is Flux's equivalent of Argo's sync waves, except it works between top-level objects rather than inside one sync.

Two things called Kustomization

Flux's Kustomization (kustomize.toolkit.fluxcd.io) is not kustomize's Kustomization (kustomize.config.k8s.io). The Flux one points at a directory; that directory may contain the other one. Exam tasks love this ambiguity, and kubectl get kustomizations.kustomize.toolkit.fluxcd.io versus reading a file resolves it.

Kustomization fields that decide behaviour

FieldEffect
intervalhow often it re-applies; drift correction is a side effect of re-applying, there is no selfHeal toggle
prunedelete resources removed from git; same blast radius as Argo's
wait / healthChecks / timeoutblock Ready until the listed objects are healthy; this is what makes dependsOn meaningful
targetNamespaceoverride the namespace for everything the path applies
postBuild.substitute / substituteFromvariable substitution from ConfigMaps/Secrets after kustomize build; Flux's answer to "one manifest, per-cluster values"
serviceAccountNameapply as that SA, the multi-tenancy control: a tenant's Kustomization cannot exceed its own RBAC
decryptionSOPS: decrypt secrets in the repo at apply time

HelmRelease, in one paragraph

It references a chart (from a HelmRepository, a GitRepository, or an OCIRepository), sets values inline and/or valuesFrom ConfigMaps and Secrets, and controls failure behaviour with install.remediation and upgrade.remediation (retries, and whether to roll back). Newer versions also detect and correct drift on the release's rendered manifests. When one fails, the useful discrimination is which layer: source resolution (the chart could not be fetched), install/upgrade (Helm itself errored), or health (the release installed but the workload never became ready). Each surfaces in a different condition, and reading the right one is the whole diagnosis.

Daily verbs

flux get sources git                      # and: helm, oci, bucket, all
flux get kustomizations                   # Ready, revision, last applied
flux reconcile kustomization demo --with-source   # force the loop now, refetch first
flux suspend kustomization demo           # the sanctioned pause for surgery
flux resume kustomization demo
flux tree kustomization demo              # what did this thing create
flux events --for Kustomization/demo
flux logs --level=error --all-namespaces
flux trace deploy/demo -n flux-demo       # which Flux object owns this resource

flux trace is the reverse lookup: hand it any live resource and it tells you which Kustomization or HelmRelease put it there. On an unfamiliar cluster that is the fastest orientation command Flux has.

Suspend is a real answer

"Stop the controller overwriting my hotfix while I debug" has a named, auditable answer in both engines: flux suspend, or disabling auto-sync in Argo. Doing it with kubectl scale deploy/kustomize-controller --replicas=0 works and will cost you the mark, because it stops everything and leaves no record on the object.

The translation table

say the same thing in either dialect
IntentArgo CDFlux
where the manifests areApplication.spec.sourceGitRepository + Kustomization.spec.path
auto-apply on changesyncPolicy.automatedimplicit: every interval
revert live driftautomated.selfHealimplicit: re-apply at interval
delete removed resourcesautomated.pruneKustomization.spec.prune
orderingsync waves + hooksdependsOn + healthChecks + wait
pausedisable auto-syncflux suspend
per-env valueskustomize overlays / Helm valuessame, plus postBuild.substitute
tenant guardrailsAppProjectserviceAccountName + namespace isolation
instant triggerwebhook to argocd-serverReceiver (notification-controller)

If you can move fluently across that table, "which tool will the exam give me" stops being a worry.

Exercises

tick the dot when its check passes

Deploy the demo base (not the overlays, which Argo owns in 2.2; two controllers fighting over one resource is a lesson, not a setup) into a Flux-owned namespace:

kubectl create ns flux-demo
flux create kustomization demo-flux \
  --source=GitRepository/platform \
  --path=./demo-app/base \
  --target-namespace=flux-demo \
  --prune=true --interval=1m \
  --health-check-timeout=2m
flux get kustomizations
verify: Ready True with an applied revision SHA, and kubectl -n flux-demo get deploy demo shows 2/2. Then flux tree kustomization demo-flux and confirm it lists exactly the deployment, service, and nothing else.

Scale the deployment by hand, then wait out one interval:

kubectl -n flux-demo scale deploy demo --replicas=5
sleep 70 && kubectl -n flux-demo get deploy demo -o jsonpath='{.spec.replicas}{"\n"}'
verify: back to 2. Same experiment as Argo's selfHeal, different mechanism; be able to say which component did it (kustomize-controller re-applying at interval).

flux suspend kustomization demo-flux, scale to 5 again, confirm it stays at 5 for two intervals, then flux resume kustomization demo-flux and confirm it snaps back.

verify: suspend shows in flux get kustomizations as Suspended True. This is the answer to "make the controller stop overwriting my hotfix while I debug".

Sources need not be git:

flux create source helm podinfo --url=https://stefanprodan.github.io/podinfo --interval=10m
flux create helmrelease podinfo --source=HelmRepository/podinfo \
  --chart=podinfo --target-namespace=flux-demo --interval=5m
kubectl -n flux-demo get deploy podinfo

Break it once for the diagnostic reps: set --chart-version='>99.0.0' on a new helmrelease, read the failure in flux get helmreleases and kubectl describe helmrelease, then delete it. Failed source resolution vs failed install vs failed health check appear in different conditions, and knowing which layer failed is the troubleshooting pattern.

verify: flux get helmreleases Ready True, release revision 1, and helm list -n flux-demo shows Flux as the release owner.

Write down, from memory, the Flux equivalent of the Argo CD Application demo-staging from 2.2 (GitRepository exists; you need one Kustomization spec: path, targetNamespace, prune, interval). Verify against flux create kustomization --export.

verify: your hand-written spec matches the generated one field for field. If you can do this translation both directions, the "which tool will the exam give me" worry disappears.

Self-check

answer before opening
Flux has no selfHeal setting. Why is drift still corrected?

Because the Kustomization re-applies its rendered manifests every interval; correcting drift is a side effect of applying, not a separate feature. The corollary: your drift window is bounded by the interval, and shortening it costs API server load.

A Kustomization is Ready False. Name the three layers where it could have failed.

Source (the GitRepository is not Ready: auth, ref, or network), build (kustomize build failed, bad path or invalid YAML), apply/health (the API server rejected something, or health checks timed out). The conditions and flux logs name the layer; do not skip straight to the manifests.

How do you order "install CRDs, then the operator, then the tenant CRs" in Flux?

Three Kustomizations with dependsOn, each with wait: true (or explicit healthChecks) so a dependency counts as satisfied only when its objects are actually healthy. Without wait/healthChecks, dependsOn only orders the apply, not the readiness.

What is postBuild.substitute for, and what is its Argo CD analogue?

Injecting per-cluster or per-environment values into manifests after kustomize build, sourced inline or from ConfigMaps/Secrets. Argo's analogues are kustomize overlays, Helm values per Application, or ApplicationSet template parameters; Flux just lets you do it without another overlay directory.

How does Flux stop one tenant's Kustomization from deploying cluster-admin-level resources?

spec.serviceAccountName: the controller impersonates that ServiceAccount when applying, so the tenant's manifests can never exceed the tenant's RBAC. Combined with disallowing cross-namespace source references, that is Flux's multi-tenancy story: the structural equivalent of Argo's AppProject.

Docs to know your way around

study time, not exam time
  • fluxcd.io: GitRepository, Kustomization, HelmRelease API references; the "flux CLI" cheat sheet; the multi-tenancy guide for the serviceAccountName pattern.
  • Offline: flux --help and flux create <kind> --help --export generate correct YAML without docs, which is faster than searching during the exam.