I've now migrated enough clients to GitOps, and inherited enough half-finished GitOps setups, to have opinions that don't come from vendor blogs. This post is the comparison I wish I could hand to every client who asks "ArgoCD or Flux?", plus the more important question they usually skip: what is GitOps actually for?

What GitOps buys you (and what it doesn't)

GitOps means: the desired state of your cluster lives in git, and a controller inside the cluster continuously pulls and reconciles toward it. Three concrete wins:

  1. Audit for free. "What changed in prod and who approved it" becomes git log. During one incident review, we reconstructed the exact sequence of a bad rollout in ten minutes from merge timestamps. Try that with kubectl apply from laptops.
  2. Drift correction. Someone hot-fixes a deployment with kubectl edit at 23:00; the controller reverts it within minutes. This sounds annoying until you've debugged a cluster where reality diverged from the repo eight months ago and nobody knows which side is correct anymore.
  3. Rollback as revert. git revert, merge, done. The rollback path is identical to the deploy path, which means it's actually tested, unlike the runbook nobody has rehearsed.

What GitOps does not buy you: progressive delivery (separate tooling), secrets management (see below), or discipline. If your team merges to main without review, GitOps just automates the propagation of mistakes.

The honest comparison

Both tools are CNCF-graduated, both are production-grade, both will be fine. The differences are real but they're about fit, not quality.

Dimension ArgoCD Flux
UI Excellent built-in web UI: resource tree, sync status, diffs, one-click rollback None built-in; CLI-first (flux get ...), optional Weave GitOps UI or Headlamp plugin
Multi-tenancy AppProjects: strong RBAC boundaries, SSO integration, per-project repo/cluster/namespace allowlists Namespace-scoped by design, leans on native Kubernetes RBAC; clean but more assembly required
Helm handling Renders charts with helm template, then applies: hooks behave subtly differently than native Helm Uses Helm SDK properly: real releases, helm list works, hooks and tests behave exactly as Helm intends
Kustomize handling First-class First-class (it's a core controller)
Secrets story No native answer; pair with External Secrets Operator or Sealed Secrets; plugins for SOPS are clunky Native SOPS decryption in kustomize-controller, genuinely elegant if you like SOPS
Resource footprint Heavier: API server, repo server, application controller, Redis; roughly 1 GB+ memory baseline Lighter: a handful of small controllers; comfortable on small/edge clusters
Sync model App-centric: an Application CR per deployable Source-centric: GitRepository/OCIRepository + Kustomization/HelmRelease compose
Image automation Argo CD Image Updater (separate, historically rough edges) Built-in image automation controllers, mature

The Helm row deserves emphasis because it surprises people: ArgoCD does not really run Helm. It templates the chart and manages raw manifests. Ninety percent of the time this is invisible; the tenth time, a chart relying on install hooks or lookup functions behaves differently than it did under helm install, and you'll lose an afternoon to it. Flux's HelmRelease is a faithful Helm client.

How I pick, per client

My decision usually comes down to three questions:

Who operates this after I leave? If the client has a team that lives in kubectl and wants everything as CRDs and CLI, Flux fits their culture. If the team includes developers who need to see their deployment (is it green, why is it degraded, what's the diff), ArgoCD's UI pays for its resource cost many times over. For most of our smaller clients, the UI is the feature. It turns "ask the DevOps person" into "look at the dashboard," and that offloading is precisely what a small consultancy should be engineering for.

Multi-tenant or single team? A platform serving multiple teams with different permission boundaries: ArgoCD's AppProjects plus SSO groups gets you there fastest. A single team, or cluster-per-team topology: Flux's simplicity wins.

Constrained clusters? Edge, on-prem with tight resources, dozens of small clusters: Flux's footprint matters. I ran ArgoCD on a client's 3×4GB bare-metal cluster once. Once.

Rough tally across our engagements: ArgoCD maybe two out of three times, almost always because of the UI and multi-tenancy; Flux when the team is strong on Kubernetes, uses SOPS already, or runs many small clusters.

Failure modes I keep meeting

The tools rarely fail. The patterns around them do.

App-of-apps sprawl. ArgoCD's app-of-apps pattern (an Application that deploys Applications) is powerful and turns into an unnavigable Russian doll by the time it's four layers deep. I inherited a setup with 140 Applications generated through three nested layers, where answering "what deploys service X's config" required tracing through four repos. Rule of thumb: two layers maximum, a root app and its children. If you need more, you want ApplicationSets with generators, which express "one app per cluster/team/directory" declaratively instead of recursively.

Sync loops. The cluster shows perpetual drift; the controller re-applies forever; something mutates the resource back. Classic causes: a mutating webhook injecting fields, HPA fighting a hardcoded replicas in git (delete replicas from the manifest), a controller adding annotations, two GitOps tools (or GitOps plus Helm-run-from-CI) managing the same resource. That last one is the worst: symptoms look like haunted infrastructure. Diff calmly, find the second writer, remove it. ArgoCD's ignoreDifferences and Flux's drift-detection ignores are for the webhook cases, not for silencing problems you haven't diagnosed.

Secrets improvisation. Git is the source of truth, secrets can't go in git plaintext, and every team solves this at the last minute. Sealed Secrets works but couples secrets to one cluster's key and makes rotation a ceremony. SOPS + Flux is clean if the team can manage KMS keys. My default recommendation for AWS-based clients is External Secrets Operator: secrets live in AWS Secrets Manager, git contains only ExternalSecret references, rotation happens where secrets rotation belongs. The controller choice barely matters here. What matters is deciding before the first kubectl create secret lands in prod with no record of how.

Pipeline half-migration. The team adopts ArgoCD but keeps kubectl apply steps in Jenkins "temporarily." Now there are two deployment truths and the GitOps audit trail is fiction. Migrate a service fully or don't start it; the hybrid state is worse than either endpoint.

The boring conclusion

Pick either, commit fully, keep the repo structure shallow, and solve secrets on day one rather than day ninety. ArgoCD when humans need to see; Flux when controllers need to compose. And remember that the git discipline of small PRs, reviews, and meaningful history is doing more of the work than whichever controller you install. GitOps is a workflow with software attached, not the other way around.