Flux (Flux CD / GitOps Toolkit): A Comprehensive Guide¶
1. What Flux Is¶
Flux (now "Flux CD," built on the GitOps Toolkit) is a CNCF graduated GitOps engine composed of a set of small, single-purpose Kubernetes controllers, each reconciling its own CRD. Where ArgoCD centralizes reconciliation logic in one Application Controller, Flux decomposes GitOps into composable primitives: fetch a source, then apply it. This makes Flux feel more like "native Kubernetes operators" and less like a standalone platform bolted onto the cluster.
Flux v2 (the current generation, a full rewrite of Flux v1) is what's referenced throughout this guide — Flux v1 is EOL.
2. Architecture¶
Flux ships as a collection of independently-releasable controllers, installed together via flux bootstrap or individually via Helm/manifests:
| Controller | CRDs it owns | Role |
|---|---|---|
| source-controller | GitRepository, HelmRepository, HelmChart, Bucket, OCIRepository |
Pulls artifacts from Git, Helm repos, S3-compatible buckets, or OCI registries. Produces a versioned tarball artifact + revision hash consumed by other controllers. This is the only controller that talks to external sources — a deliberate security boundary. |
| kustomize-controller | Kustomization |
Applies rendered Kustomize output (or plain manifests, which are valid degenerate Kustomize) from a source artifact to the cluster. Handles pruning, health checks, dependency ordering (dependsOn), and post-build variable substitution. |
| helm-controller | HelmRelease |
Reconciles Helm releases — installs/upgrades/rolls back based on a HelmChart produced by source-controller, tracking values and remediation policy. |
| notification-controller | Provider, Alert, Receiver |
Outbound alerts (Slack, Teams, Discord, generic webhook, etc.) on reconciliation events, and inbound webhook Receivers to trigger immediate reconciliation (vs. waiting for the poll interval). |
| image-reflector-controller | ImageRepository, ImagePolicy |
Scans container registries and evaluates tag policies (semver, alphabetical, numerical). |
| image-automation-controller | ImageUpdateAutomation |
Writes updated image tags back to Git automatically based on the ImagePolicy — closing the loop for "new image pushed → manifest updated → cluster deployed" without a human commit. |
All controllers are independently scalable and can be installed à la carte — e.g., you can run just source-controller + kustomize-controller without Helm support at all.
3. Installation¶
The idiomatic path is flux bootstrap, which installs the controllers and commits the Flux manifests themselves into your Git repo, so Flux manages its own upgrade via GitOps from day one:
flux bootstrap github \
--owner=my-org \
--repository=fleet-infra \
--branch=main \
--path=clusters/production \
--personal
This creates a deploy key, pushes the Flux system manifests to clusters/production/flux-system/, and sets up a GitRepository + Kustomization pointing Flux at itself. Equivalent bootstrap providers exist for GitLab, Bitbucket, Azure DevOps, and generic Git servers.
Alternative: install via the flux2 Helm chart for environments wanting Helm-native lifecycle management instead of the bootstrap self-management model.
4. Core Concepts¶
4.1 GitRepository (Source)¶
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: my-repo
namespace: flux-system
spec:
interval: 1m
url: https://github.com/org/repo.git
ref:
branch: main
secretRef:
name: repo-credentials
Produces an artifact other controllers reference by name — decoupling "where config lives" from "what applies it."
4.2 Kustomization (Apply)¶
Not to be confused with a plain Kustomize kustomization.yaml — this is Flux's CRD wrapping that concept:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-service
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: my-repo
path: ./k8s/my-service
prune: true
wait: true
timeout: 3m
dependsOn:
- name: infra-base
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: my-service
namespace: my-service
prune: true— mirrors ArgoCD's pruning; deletes resources removed from Git.dependsOn— declarative ordering between Kustomizations (Flux's analog to sync waves, but expressed as a DAG between whole Kustomization units rather than per-resource annotations).postBuild.substitute/substituteFrom— variable substitution against ConfigMaps/Secrets at apply time, Flux's answer to templating plain YAML without a templating engine.
4.3 HelmRelease¶
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: my-chart
namespace: my-service
spec:
interval: 10m
chart:
spec:
chart: my-chart
version: '>=1.2.0 <2.0.0'
sourceRef:
kind: HelmRepository
name: my-charts
namespace: flux-system
values:
replicaCount: 3
install:
remediation:
retries: 3
upgrade:
remediation:
remediateLastFailure: true
Flux's Helm handling is a first-class controller reconciling a live HelmRelease object, with automatic rollback/remediation policies — more deeply "Kubernetes-native" than shelling out to helm template for diffing, though it also means Helm hooks and lifecycle quirks are mediated through Flux's own semantics rather than the Helm CLI directly.
4.4 Multi-Tenancy¶
Flux's default tenancy model relies on Kubernetes-native primitives rather than a bespoke Project CRD:
- Each Kustomization/HelmRelease can specify a serviceAccountName, so the reconciliation itself runs under that SA's RBAC — a tenant literally cannot deploy what their SA lacks permission for. This is enforced by the Kubernetes API server itself, not by Flux's own policy engine.
- Namespace-scoped controllers can be run per-tenant for stronger isolation (--watch-all-namespaces=false plus per-tenant controller instances), though most installs run one shared set of controllers watching cluster-wide.
- Combine with Kustomization.spec.targetNamespace and .spec.serviceAccountName for the common "platform team owns cluster-scoped resources, app teams own their namespace" split.
This is philosophically different from ArgoCD's AppProject — Flux leans on existing Kubernetes RBAC rather than introducing a parallel authorization model.
4.5 Image Automation¶
The one feature area where Flux is meaningfully ahead of stock ArgoCD (which needs the separate Argo CD Image Updater project to match this):
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: my-service
spec:
image: ghcr.io/org/my-service
interval: 1m
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: my-service
spec:
imageRepositoryRef:
name: my-service
policy:
semver:
range: '>=1.0.0'
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: my-service
spec:
sourceRef:
kind: GitRepository
name: my-repo
git:
commit:
author:
name: fluxcdbot
update:
path: ./k8s/my-service
strategy: Setters
Marker comments in your YAML (# {"$imagepolicy": "flux-system:my-service"}) tell the automation controller exactly which field to rewrite, and it commits the change back to Git directly — a genuine closed loop from registry push to Git commit to cluster apply.
5. Rendering Engines Supported¶
- Native Kustomize (first-class, since
Kustomizationis the apply primitive) - Helm via
HelmRelease - Plain manifests (a directory with no
kustomization.yamlis treated as an implicit flat Kustomization) - No native Jsonnet/cdk8s support — you'd pre-render externally and commit plain YAML, or use a CI step to generate manifests into the watched path.
6. Notifications and Webhooks¶
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
name: slack
spec:
type: slack
address: https://hooks.slack.com/services/...
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
name: on-call
spec:
providerRef:
name: slack
eventSeverity: error
eventSources:
- kind: Kustomization
name: '*'
Flux also supports inbound Receiver objects — a webhook endpoint that, when hit (e.g., by a GitHub push webhook), triggers immediate reconciliation of the associated source instead of waiting for the poll interval. This gets you push-based responsiveness without sacrificing the pull-based security model (the webhook only ever triggers "go check Git again," it never pushes manifests directly).
7. Multi-Cluster Management¶
Flux has no built-in hub/spoke UI or fleet dashboard in the open-source core (that capability — Flux fleet management with a UI/dashboard — lives in Weaveworks-descended commercial/semi-commercial tooling, e.g., Weave GitOps, and more recently in the CNCF-donated Flux Operator / flux-subsystem-argo hybrid options). The standard OSS pattern is:
- Run Flux per-cluster, bootstrapped from a shared monorepo (fleet-infra) with a clusters/<cluster-name>/ directory per cluster.
- Each cluster's Flux instance only ever reconciles its own directory — there's no cross-cluster control plane by default, which is a stronger isolation stance than ArgoCD's hub model but means no built-in single-pane-of-glass without extra tooling.
- Cross-cluster promotion (dev → staging → prod) is handled by directory/branch conventions and CI, not a Flux-native primitive.
8. CLI Essentials¶
flux bootstrap github --owner=org --repository=fleet-infra --path=clusters/prod
flux get sources git
flux get kustomizations
flux get helmreleases -A
flux reconcile source git my-repo
flux reconcile kustomization my-service --with-source
flux suspend kustomization my-service
flux resume kustomization my-service
flux logs --follow --level=error
flux diff kustomization my-service --path ./k8s/my-service
9. Operational Best Practices¶
- Structure repos around
clusters/<name>/with sharedbase/overlays — this is the canonical Flux monorepo layout and pairs naturally with Kustomize overlays for per-cluster patches. - Use
dependsOndeliberately to sequence CRDs → operators → workloads, mirroring what ArgoCD does with sync-waves but as an explicit DAG between Kustomizations rather than per-resource weights. - Prefer OCIRepository over GitRepository for large/binary artifacts — Flux can source from OCI-compliant registries (including plain OCI artifacts, not just Helm charts), useful if your pipeline already pushes packaged manifests as OCI artifacts.
- Suspend, don't delete, during incident response —
flux suspend kustomizationfreezes reconciliation without tearing anything down, letting you hand-debug live state safely. - Isolate tenant blast radius with
serviceAccountName+ scoped RBAC, since Flux won't stop a misconfigured Kustomization the way anAppProjectrestriction would in ArgoCD — the guard rail here is Kubernetes RBAC, so it has to actually be configured per-tenant. - Use
--watch-all-namespaces=falsewith a controller instance per tenant namespace in strict multi-tenant environments where even shared-controller blast radius is unacceptable.
10. Troubleshooting¶
| Symptom | Likely Cause |
|---|---|
GitRepository stuck False |
Auth failure (deploy key/token expired) or the ref branch/tag doesn't exist — check flux get sources git for the condition message. |
Kustomization stuck Progressing |
A healthCheck target never reports ready, or dependsOn is waiting on an upstream Kustomization that itself is failing. |
HelmRelease stuck in remediation loop |
Chart values invalid or a pre-install hook failing repeatedly; check flux get helmreleases and kubectl describe helmrelease. |
| Image automation not committing | Marker comment mismatched to the ImagePolicy name/namespace, or the automation's GitRepository write-access deploy key lacks push permission. |
| Changes not appearing | Poll interval hasn't elapsed — flux reconcile forces an immediate check instead of waiting. |
11. Where Flux Excels¶
- Lightweight, à la carte controllers — install only what you need.
- First-class, tightly integrated image automation (registry → Git → cluster) without bolting on a separate project.
- Leans on native Kubernetes RBAC for tenancy rather than a parallel authorization model — fewer new concepts for teams already fluent in K8s RBAC.
- Self-management via bootstrap (Flux upgrades itself through the same GitOps loop it manages everything else with).
- Strong OCI-artifact-native sourcing model, useful in registries-as-source-of-truth pipelines.