The Problem GitOps Solves (And Why Your Colleague Is the Problem)
Picture a Kubernetes cluster that’s been running for two years. Services are deployed. Things mostly work. Then something breaks. You open the deployment to investigate and discover that the YAML in the cluster doesn’t match the YAML in Git. At all. There’s an environment variable that doesn’t exist in any repository. There’s a replica count that got bumped from 2 to 5 at some point. No PR. No commit. No audit trail.
Your colleague meant well. There was an incident, things were moving fast, they ran kubectl apply -f on a local file and never committed the change. Classic. No malice, just the entirely human tendency to fix the immediate problem and defer the paperwork.
GitOps is the systematic answer to this. The principle: Git is the single source of truth. The cluster should always match what’s in the repository. Any change to the cluster must happen through Git. An automated reconciliation loop continuously checks for drift and corrects it.
Two tools implement this on Kubernetes: ArgoCD and Flux. They have similar goals and different philosophies about how to get there.
What GitOps Actually Means (Practically)
Before comparing the tools, it’s worth being precise about what GitOps requires:
- Declarative configuration: everything about your application’s deployment lives in YAML files in a Git repository
- Versioned, auditable history: every change has a commit, an author, and a timestamp
- Automated reconciliation: a controller running in the cluster watches the repo and applies changes when they differ from cluster state
- Correctness by default: if someone manually edits a resource in the cluster, the controller reverts it to match Git
The last point is the controversial one for teams used to hotfixing in production. GitOps makes that harder by design. The correct fix is always: update Git, let the reconciliation loop apply it. The discipline is the point.
ArgoCD: The UI-Focused One
ArgoCD is a Kubernetes-native GitOps tool that puts a beautiful web UI front and center. You can see every application, its sync status, whether it’s healthy, and a live graph of what’s running in the cluster versus what Git says should be running. For teams new to GitOps, the visual feedback is enormously helpful.
Key ArgoCD Concepts
Application: an ArgoCD custom resource that defines what to sync — a source (Git repo + path) and a destination (cluster + namespace).
App-of-Apps pattern: a parent Application that manages other Applications, allowing you to bootstrap an entire cluster’s worth of deployments from a single ArgoCD application definition.
Sync policies: manual sync (you click a button or run a CLI command) or automated sync (ArgoCD watches for changes and applies them automatically, optionally with pruning of removed resources).
Installing ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Get the initial admin password:
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d && echo
Access the UI (port-forward for initial setup):
kubectl port-forward svc/argocd-server -n argocd 8080:443
Navigate to https://localhost:8080, log in with admin and the retrieved password.
Defining an Application
# my-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/yourorg/your-gitops-repo
targetRevision: main
path: apps/my-app
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated:
prune: true # Remove resources deleted from Git
selfHeal: true # Revert manual changes to cluster
syncOptions:
- CreateNamespace=true
Apply this and ArgoCD takes over. Any commit to apps/my-app in your repo triggers an automatic sync. Any manual kubectl edit to a managed resource gets reverted.
ArgoCD RBAC
ArgoCD has a built-in RBAC system that maps to SSO providers (GitHub, GitLab, LDAP, OIDC). This matters for teams: you can give developers read-only access to see app status without giving them sync permissions, and restrict sync to CI pipeline service accounts.
Flux: The Automation-First One
Flux takes the opposite philosophy from ArgoCD: there’s no UI. Flux is a set of Kubernetes controllers that you configure entirely through custom resources and the flux CLI. It’s designed for automation-heavy environments where you want things to happen without human checkpoints.
Flux is more composable than ArgoCD — it breaks GitOps functionality into discrete controllers (source controller, kustomize controller, helm controller, image automation controller) that can be used independently. This makes it more flexible and more complex.
Key Flux Concepts
GitRepository: a source custom resource that Flux polls for changes.
Kustomization: a Flux reconciliation unit — it takes a GitRepository source and applies it to the cluster using Kustomize.
HelmRelease: like a Kustomization but for Helm charts. Flux’s helm controller manages chart deployment and upgrades.
Image Automation: Flux can scan container registries for new image tags and automatically update your Git repository with the new tag, then sync the change to the cluster. This is Flux’s killer feature for CI/CD pipelines — new image → Flux updates Git → Flux deploys it.
Installing Flux
The flux CLI bootstraps everything:
# Install the flux CLI
curl -s https://fluxcd.io/install.sh | sudo bash
# Bootstrap Flux on your cluster (GitHub example)
flux bootstrap github \
--owner=yourorg \
--repository=your-gitops-repo \
--branch=main \
--path=./clusters/my-cluster \
--personal
Bootstrap creates the Flux controllers in the cluster AND commits the Flux system manifests to your repo, so the Flux installation itself is version-controlled. This is a nice design choice.
Defining a Flux Source and Kustomization
# /clusters/my-cluster/apps/source.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: my-app
namespace: flux-system
spec:
interval: 1m
url: https://github.com/yourorg/your-gitops-repo
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app
namespace: flux-system
spec:
interval: 10m
path: ./apps/my-app
prune: true
sourceRef:
kind: GitRepository
name: my-app
Flux polls the GitRepository every minute and applies the Kustomization every 10 minutes (or immediately on detected change).
Image Automation: Flux’s Superpower
Flux can watch a container registry, detect new image tags, update your Git repo, and deploy — all automatically:
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: my-app
namespace: flux-system
spec:
image: registry.example.com/my-app
interval: 5m
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: my-app
namespace: flux-system
spec:
imageRepositoryRef:
name: my-app
policy:
semver:
range: ">=1.0.0"
When a new image matching the policy appears, Flux updates the deployment YAML in Git and applies it. Your CI pipeline just needs to push an image — Flux handles the rest.
ArgoCD vs Flux: Side by Side
| Feature | ArgoCD | Flux |
|---|---|---|
| Web UI | Yes (excellent) | No (CLI only, plus Weave GitOps) |
| Multi-cluster | Yes | Yes |
| Helm support | Yes | Yes (HelmRelease CRD) |
| Kustomize support | Yes | Yes (first-class) |
| RBAC | Built-in, SSO support | Kubernetes RBAC |
| Image automation | Via Argo Image Updater (addon) | Built-in, mature |
| App-of-Apps pattern | Native | Via Kustomization composition |
| Drift detection | Yes, visual | Yes, automated |
| Self-healing | Yes (selfHeal policy) | Yes (reconciliation loop) |
| Installation complexity | Medium | Low (flux bootstrap CLI) |
| Learning curve | Lower (UI helps) | Higher (all CRDs and CLI) |
| Community | Large, CNCF graduated | Large, CNCF graduated |
| Best for | Teams new to GitOps | Automation-heavy pipelines |
When to Use Each
Choose ArgoCD when:
- Your team is new to GitOps and the visual feedback helps adoption
- You have multiple teams with different permission levels needing a shared view
- You want app-of-apps to bootstrap large clusters
- You’re in a regulated environment where humans reviewing deploys before they go live is a requirement
Choose Flux when:
- You want full image automation — new image in registry → automatic Git commit → automatic deploy
- Your team is comfortable with Kubernetes primitives and prefers everything-as-code
- You want Flux to be bootstrapped and version-controlled in Git like any other cluster resource
- You’re building a fully automated delivery pipeline with minimal human checkpoints
Both tools are CNCF graduated projects with strong communities. You’re not making a bet on vaporware. Pick based on your team’s workflow, not on hype. And both of them will prevent the scenario where your colleague applies YAML directly to production at 2am and forgets to commit it. That alone is worth the adoption cost.