After running both in production across a dozen clusters, here's where Flux and Argo CD actually differ and which one we'd reach for now.
We inherited a Flux v1 setup that nobody understood and an Argo CD instance that three teams were fighting over. Consolidating them forced a real decision instead of a whiteboard debate, and the answer wasn't what the loudest engineer wanted. Here's what actually mattered once both were running the same workloads.
Argo CD models everything as an Application. You point it at a repo path, and it reconciles the live cluster state against the manifests. The mental model is object-centric: you have a thing called an app, it has a health status, you look at it in a UI.
Flux is a set of controllers that each do one job. source-controller pulls the Git repo, kustomize-controller applies it, helm-controller handles charts, notification-controller sends alerts. There's no single "app" object unless you build one. It's more Unix-y, more composable, and there's less magic to reason about when something breaks.
A minimal Flux setup looks like this:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: platform
namespace: flux-system
spec:
interval: 1m
url: https://github.com/acme/platform-config
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 5m
path: ./clusters/prod
prune: true
sourceRef:
kind: GitRepository
name: platform
The equivalent Argo Application:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: apps
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/acme/platform-config
targetRevision: main
path: clusters/prod
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
Note selfHeal: true in Argo. That's the setting that reverts manual kubectl edit changes back to Git. Flux does this by default through its reconcile loop; you turn it off with spec.suspend. Different defaults, same capability.
Argo CD's UI is the reason most teams pick it, and I'll be honest, it earns that. The topology view showing pods, replicasets, and ingress with live health status is genuinely useful during an incident. When a rollout is stuck, you see the failing resource in two clicks. Non-platform engineers can self-serve a sync without touching a terminal.
Flux has no first-party UI. You get flux get kustomizations on the CLI and whatever you build into Grafana from its metrics. The Weave GitOps dashboard existed but its future is murky after the Weaveworks shutdown, so I wouldn't bet a platform on it in 2026. If your developers expect a dashboard, that's a real cost with Flux.
This is where Flux won for us. Each Kustomization and GitRepository is a namespaced object with its own service account, so you can hand team A a namespace and let them run their own reconciliation without any chance of touching team B's resources. RBAC does the enforcement, and it's Kubernetes-native RBAC, nothing bespoke.
Argo does multi-tenancy through AppProject objects and its own RBAC layer on top of Kubernetes RBAC. It works, but it's a second permission system to reason about, and we caught two misconfigurations where an app could sync to a namespace it shouldn't. Not Argo's fault exactly, but the extra layer is extra surface to get wrong.
Argo CD's application controller is a single point of scale. Past a few thousand applications it starts to sweat, and you shard the controller across replicas, which is fiddly. Flux scales more naturally because the controllers are independent, but debugging a stuck reconcile means reading controller logs and correlating events, which is slower than glancing at a UI.
Both handle Helm well now. Both do image automation, though Flux's image-reflector-controller for auto-bumping tags is more mature than Argo Image Updater, which still feels bolted on.
For a platform team running many clusters with strong tenant isolation and engineers who live in the CLI, Flux. It's smaller, composable, and the multi-tenancy story is cleaner. For an org where application teams need to see and drive their own deploys, and where the dashboard reduces support tickets, Argo CD, and it's not close. We ended up standardizing on Argo for the product clusters because the developer self-service saved our platform team hours a week, and kept Flux only on the infrastructure clusters where no human needs a UI.
Get the latest tutorials, guides, and insights on AI, DevOps, Cloud, and Infrastructure delivered directly to your inbox.
Both put SQLite near your users, but they solve replication and write latency very differently. We ran the same schema on both for a month and picked one.
We had long-lived AWS keys sitting in a datacenter we don't own. IAM Roles Anywhere let us delete every one of them. Here's the real setup.
Explore more articles in this category
We used to ship code and turn it on in the same breath, so every deploy was a bet. Feature flags split those two events apart and made rollbacks a config toggle.
Our best engineer quit citing on-call. We rebuilt the whole thing: saner rotations, runbooks that actually help at 3am, and escalation that doesn't punish asking for help.
Our early postmortems quietly assigned blame and taught people to hide mistakes. Here's the template and the facilitation rules that finally made them honest and useful.
Evergreen posts worth revisiting.