GitOps with Flux: Continuous Delivery for Kubernetes
Most CI/CD pipelines end with a step that pushes changes into a cluster - a kubectl apply or helm upgrade running from the pipeline's context, with credentials to reach production. GitOps inverts this: instead of pipelines pushing into the cluster, a controller inside the cluster continuously pulls the desired state from Git and reconciles reality to match it. Flux is one of the two dominant tools that implement this (the other being ArgoCD).
Why pull instead of push
With a push model, your CI system needs credentials that can reach and modify production - meaning a compromised pipeline, or a leaked CI variable, is a direct path into your cluster. With a pull model, the controller lives inside the cluster and only needs read access to your Git repository; nothing external holds standing write credentials to production. As a side effect, Git becomes an honest, complete record of what's actually running, since nothing gets applied any other way.
Flux's building blocks
Flux is built from several controllers, each responsible for one part of the reconciliation loop:
- source-controller - watches a Git repository (or Helm repository, or S3 bucket) and fetches its contents when it changes. This is a GitRepository resource.
- kustomize-controller - takes the fetched source, applies a Kustomize build, and reconciles the result into the cluster. This is a Kustomization resource.
- helm-controller - same idea, but for Helm charts, via HelmRelease resources.
- notification-controller - sends alerts (Slack, webhooks) when reconciliation succeeds or fails.
A minimal setup looks like this:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: my-app
namespace: flux-system
spec:
interval: 1m
url: https://github.com/myorg/my-app-config
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app
namespace: flux-system
spec:
interval: 5m
path: "./k8s/production"
prune: true
sourceRef:
kind: GitRepository
name: my-app
The GitRepository says "watch this repo." The Kustomization says "take the k8s/production folder from that source, and make the cluster match it" - checking every 5 minutes (interval), and importantly, prune: true means resources removed from Git get removed from the cluster too, not just left behind.
Bootstrapping
In practice, you don't hand-write all of this from scratch - the flux bootstrap command installs Flux's controllers into a cluster and commits the manifests for Flux to manage itself back into your Git repo, so Flux ends up managing its own configuration the same way it manages everything else:
flux bootstrap github \
--owner=myorg \
--repository=my-app-config \
--branch=main \
--path=clusters/production
From that point on, changing what's deployed is a matter of committing to that repository - there's no separate deploy command to run.
What reconciliation actually catches
Because Flux continuously reconciles rather than just applying once, it also catches drift - if someone manually edits a resource with kubectl edit (a quick fix during an incident, say), Flux will notice the live state no longer matches Git on its next reconciliation pass and revert it back. This is a feature, not a bug: it means Git genuinely stays the source of truth, but it also means "temporary" manual changes made outside Git don't stay temporary - they just get undone, so any real fix needs to go through Git.
Flux vs ArgoCD, briefly
Both implement the same GitOps pull-based model and solve the same core problem. The practical differences are more about experience than capability: ArgoCD ships with a polished web UI out of the box, which many teams weigh heavily during evaluation. Flux is more CLI/CRD-first and tends to feel more like "just another Kubernetes controller" - a reasonable fit if your team already thinks in kubectl and YAML rather than wanting a dedicated dashboard. Neither is a wrong choice; teams have shipped production systems reliably on both.
Where to go next
GitOps is as much a workflow change as a tool choice - the core idea (Git as the single source of truth, a controller reconciling toward it) matters more than which specific tool implements it. If you haven't already, our Kubernetes fundamentals guide covers the Pods/Deployments/Services concepts that Flux is ultimately reconciling. Then test what you know with the Kubernetes quiz on OpsQuiz.