Andrew Mercer
on this page

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 Kustomization is the apply primitive)
  • Helm via HelmRelease
  • Plain manifests (a directory with no kustomization.yaml is 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 shared base/ overlays — this is the canonical Flux monorepo layout and pairs naturally with Kustomize overlays for per-cluster patches.
  • Use dependsOn deliberately 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 responseflux suspend kustomization freezes 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 an AppProject restriction would in ArgoCD — the guard rail here is Kubernetes RBAC, so it has to actually be configured per-tenant.
  • Use --watch-all-namespaces=false with 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.