If you have been following the GitOps conversation on this blog, you have already seen one half of it: the OpenTofu + Argo CD post walks through bootstrapping k3s and handing day-one operations to Argo CD, and the Compose vs Kubernetes post settles when a cluster earns its keep at all.
What neither post answered is the question everyone asks the moment the cluster is real: Argo CD or Flux? I have now operated both long enough that the feature tables stopped being useful, because the tables are written by people who read the docs, and the differences only show up after you have run them for a while.
I cover the bootstrap flow on both sides, how each one really handles Helm, where secrets get decrypted, how multi-cluster actually works, what drift detection does when you are not looking, the image automation story, disaster recovery with real commands, the UI and API surface, and the repo structure that makes each one click.
The short answer up front: both are CNCF graduated, both reconcile from git, and the choice is mostly about who operates the cluster and how many control-plane components you want to babysit.
The short version
- Argo CD is a centralized application platform; Flux is a set of distributed controllers. Argo CD runs one control plane (server, repo-server, application-controller, Redis, database) that can manage many clusters. Flux runs per-cluster controllers with no central coordinator, which changes the blast radius and the RBAC story.
- Bootstrap is the first place the philosophies show. flux bootstrap is one command that creates the repo, installs the controllers, and commits the manifests. Argo CD is a Helm chart, then you wire the app-of-apps handoff by hand.
- Helm works on both, in different shapes. Argo CD renders charts in the repo-server and tracks the rendered result. Flux manages HelmRelease objects natively with the helm-controller, and v2.9 added post-render strategies and chart hooks support.
- Secrets stay out of git on both, but decryption lives in different places. Flux decrypts SOPS files natively at apply time, with Age support including the post-quantum cipher in v2.9. Argo CD needs a sidecar plugin or a Kustomize secret generator plus the age key mounted into the repo-server.
- Multi-cluster is the biggest architectural fork. Argo CD is hub-and-spoke: one control plane, many registered clusters, central RBAC. Flux is distributed: every cluster runs its own controllers and reads its own git paths.
- Drift handling differs in the default. Argo CD self-heals only when you turn selfHeal on, and it diffs against a watch-backed cache. Flux reconciles on an interval and applies the desired state, with server-side apply and field ignore rules in v2.9.
- Image automation is a bolt-on on Argo and core on Flux. argocd-image-updater is a separate project you install; Flux ships image-reflector-controller and image-automation-controller in the toolkit.
- Disaster recovery is the part nobody tests, and the two stories are not the same. Flux recovery is re-running bootstrap against the repo and restoring the flux-system secret. Argo CD recovery means exporting the applications, projects, and secrets the control plane holds, because git alone does not reconstruct the registry.
- The UI is the real differentiator for teams. Argo CD ships a full web UI with SSO and RBAC. Flux ships no official UI; you live in kubectl, the CLI, and the Kubernetes API, with the Flux Web UI as a separate project.
- Argo CD
3.5.x (3.5 GA 2026-08-04; latest patch 3.5.2) - Flux
v2.9.x (2.9 GA June 2026; latest patch 2.9.4) - Argo CD Helm chart
argo-cd chart 10.x line - argocd-image-updater
Separate project, current at time of writing
Checked 2026-08-27 against the GitHub releases pages for argoproj/argo-cd and fluxcd/flux2, endoflife.date/argo-cd, and fluxcd.io release notes. Both projects ship fast; re-check versions before you rely on them.
What each tool actually is
Argo CD: a centralized application platform
Argo CD started at Intuit in 2018 and graduated as a CNCF project in December 2022. The architecture is a control plane: the argocd-server exposes the UI and API, the argocd-repo-server fetches git and renders manifests (plain YAML, Kustomize, Helm, Jsonnet), and the argocd-application-controller watches both the git state and the live cluster through a cached informer, then syncs when they drift. The unit of management is the Application: a CR that says which repo, path, revision, and destination cluster to reconcile. A project groups applications and defines RBAC boundaries.
Flux: a toolkit of controllers
Flux v2, the GitOps Toolkit, came out of Weaveworks and also graduated as a CNCF project in 2022. There is no central control plane. Each cluster runs a set of controllers: source-controller pulls git, OCI artifacts, or buckets; kustomize-controller applies Kustomize or plain YAML; helm-controller manages HelmRelease objects; notification-controller handles events and webhooks; image-reflector-controller and image-automation-controller do the image update loop. The API is the Kubernetes API: everything is a CRD, and the flux CLI mostly generates YAML for you.
Bootstrap: one command versus a chart plus wiring
Run both from scratch and the difference is visible in the first ten minutes.
$ flux bootstrap github \
--owner=yourname \
--repository=gitops \
--branch=main \
--path=./clusters/prod
# creates the repo if missing, installs the controllers,
# commits the manifests, and writes the kubeconfig
$ flux get kustomizations
NAME READY MESSAGE
flux-system True Applied revision: main@sha1:...What that one command did: it generated the toolkit manifests for the version you are running, installed them into the cluster, created a git repository if it did not exist, and committed the flux-system Kustomization back into git. The cluster now reconciles itself from the repo. There is no separate control plane to patch, and the same command run against a second cluster gives you the distributed model with no central state.
$ helm repo add argo https://argoproj.github.io/argo-helm
$ helm install argocd argo/argo-cd -n argocd --create-namespace
# the full bootstrap handoff is in the OpenTofu + Argo CD post;
# the short version: create the app-of-apps Application in git
$ argocd app create apps \
--repo https://github.com/yourname/gitops \
--path apps --dest-server https://kubernetes.default.svc \
--dest-namespace argocd
$ argocd app sync appsArgo CD is a platform you install, then configure: a chart, a namespace full of services, a Redis for caching, a database for app state, an admin password you rotate on day one, and a bootstrap Application that points at a directory of further Application manifests. It is more moving parts, and the payoff is the UI, the RBAC model, and the centralized view over many clusters.
Helm: two philosophies
Both tools run Helm charts, but the model differs in a way that bites in production. Flux treats a Helm release as a first-class object. You declare a HelmRelease, the helm-controller does the install, upgrade, rollback, and reconciliation, and the release state is managed the way Helm expects, including the secret-based release history.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: cert-manager
namespace: cert-manager
spec:
interval: 30m
chart:
spec:
chart: cert-manager
version: "1.17.x"
sourceRef:
kind: HelmRepository
name: jetstack
namespace: flux-system
values:
installCRDs: true
driftDetection:
mode: warn # or enabled; field-ignore rules added in v2.9Argo CD renders the chart inside the repo-server and applies the rendered output as plain manifests tracked by the Application. You point the Application at a chart in a Helm repository or a path in git, set values, and Argo CD syncs the rendered result. The release object itself is not tracked the way helm tracks it; Argo CD compares rendered manifests against the live cluster, which is a different mental model and a common source of surprise when someone runs helm rollback against an Argo-managed chart.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cert-manager
namespace: argocd
spec:
project: default
source:
chart: cert-manager
repoURL: https://charts.jetstack.io
targetRevision: "1.17.x"
helm:
valueFiles:
- values-prod.yaml
destination:
server: https://kubernetes.default.svc
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: trueFlux 2.9 added post-render strategies and chart hooks support, which closes most of the gaps for complex charts, and Argo CD 3.x keeps improving Helm handling too. The practical difference remains: Flux owns the release lifecycle, Argo CD owns rendered manifests. Pick the one that matches how your team already thinks about Helm.
Secrets: SOPS, age, and where decryption happens
The rule on both is the same: secrets do not live in git in plaintext, and the SOPS + age pattern from the Docker secrets post is the baseline that keeps working here. The difference is where decryption happens. Flux decrypts natively: the kustomize-controller reads .sops.yaml and the age key you provide, decrypts at apply time, and never writes plaintext into the repo. Flux v2.9 extended SOPS support to the Age post-quantum cipher.
# on your admin machine, generate an age key
$ age-keygen -o age.agekey
# encrypt a secret file with the public key, commit the ciphertext
$ sops --encrypt --age <public-key> secrets.enc.yaml > secrets.enc.yaml
# give the cluster the key once; Flux decrypts at apply time
$ flux create secret sops-age --age-file=age.agekey
$ kubectl -n flux-system get secret sops-ageArgo CD has no built-in SOPS. The two common paths are the argocd-cmp sidecar plugin, which runs sops during manifest generation, or a Kustomize secret generator that reads an encrypted file with an age key. Either way the age key has to be mounted into the repo-server, and you manage the plugin version yourself.
# Argo CD: SOPS via the cmp plugin, referenced from the Application
spec:
source:
plugin:
name: sops-age
# or via kustomize: a secretGenerator reading an encrypted file
# with an age key mounted into the repo-server from a secretMulti-cluster: hub-and-spoke versus distributed controllers
This is the fork that decides most real-world choices. Argo CD is hub-and-spoke: one control plane holds the registry of clusters, and the same UI, RBAC, and ApplicationSets span all of them. Flux is distributed: every cluster runs its own controllers, and there is no central credential store that can reach into every cluster at once.
# Argo CD: register clusters on the hub
$ argocd cluster add prod-ctx --name prod
$ argocd cluster list
# Flux: no registration, each cluster bootstraps its own path
$ flux bootstrap github --owner=you --repository=gitops \
--path=./clusters/prod
$ flux bootstrap github --owner=you --repository=gitops \
--path=./clusters/devThe trade-off is the one you expect: Argo CD gives you central control and a single pane of glass, at the cost of a control plane that is a high-value target and a single point of failure for every cluster it manages. Flux gives you isolation, because a compromised Flux cluster exposes only itself, and the cost is that fleet-wide changes are git-wide changes: you edit a path, not a dashboard. For a homelab that is a feature; for a platform team it is a reason to consider Argo CD.
Drift detection: who owns the cluster state
Both tools are designed to make the cluster converge to git. The difference is how aggressively, and what happens when another controller edits a resource. Argo CD keeps a watch-backed cache of live state, diffs it against the desired manifests, and shows you the diff in the UI. Sync applies the changes. selfHeal is off by default; you opt in with syncPolicy.automated.selfHeal: true, and that is when the interesting behavior starts.
Flux reconciles on an interval and applies the desired state, and it does not ask first. Manual kubectl edits are reverted on the next reconciliation, which is the behavior people either love or discover the hard way. Flux 2.9 made this more surgical with server-side apply and field ignore rules, so you can let Flux own most fields while another controller owns specific ones. Argo CD's equivalent is compare-options and ignoreDifferences.
Image automation: update the manifest when the image changes
Keeping the image tag in sync is the feature that separates the tools the most clearly. On Argo CD, image automation is a separate project, argocd-image-updater, that you install next to Argo CD. On Flux, the image-reflector-controller and image-automation-controller are part of the GitOps Toolkit, so the loop is core, not an add-on.
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: web
namespace: apps
spec:
imageRepositoryRef:
name: web
policy:
semver:
range: ">=1.0.0 <2.0.0"
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: apps
namespace: flux-system
spec:
interval: 15m
sourceRef:
kind: GitRepository
name: flux-system
update:
strategy: Setters
path: ./appsThe Flux loop: ImageRepository scans the registry, ImagePolicy applies a rule (semver, regex, or digest), and ImageUpdateAutomation writes the new tag back to git. Argo CD's image updater does the same job through annotations on the Application: it polls the registry, updates the manifest parameter, and can commit back to git or just sync. Both end with a git commit, which is the point: the update goes through the same reconcile loop as everything else.
# Argo CD: image updater annotations on the Application
metadata:
annotations:
argocd.argoproj.io/image-update.auto: "true"
argocd.argoproj.io/image-update.registry-preference: docker.io
argocd.argoproj.io/image-update.strategy: semver
argocd.argoproj.io/image-update.semver-range: ">=1.0.0 <2.0.0"The operational difference: Flux's image automation is maintained with the toolkit and upgraded with it; argocd-image-updater is a separate release train you have to track, which is exactly the kind of dependency the release-tracking workflow exists for.
Disaster recovery: what you actually need to rebuild
Git is the source of truth in both models, so a naive disaster recovery plan is reinstall and re-reconcile. The honest difference is how much state lives outside git. Flux keeps almost nothing in the cluster except the flux-system secret (the age key and git credentials). Argo CD keeps a lot: the Application and Project objects, repo credentials, SSO configuration, the Redis cache, and the database. Some of that can be rebuilt from git, and the app-of-apps pattern is designed so the bootstrap Application is in git, but the registry itself, the secret store, and the RBAC policies do not reconstruct themselves.
# Flux: the repo is the backup. Rebuild the cluster, re-bootstrap.
$ flux bootstrap github --owner=you --repository=gitops \
--path=./clusters/prod
# the flux-system secret must be restored from your backup,
# otherwise the cluster cannot decrypt its own config
# Argo CD: export what the control plane holds, then reinstall
$ argocd export --all > argocd-backup.yaml
$ kubectl -n argocd get applications,projects,secrets -o yaml > argocd-state.yaml
$ helm install argocd argo/argo-cd -n argocd
$ kubectl apply -f argocd-backup.yamlTest both. The Flux test is a clean VM, re-bootstrap, and watch it converge from git. The Argo CD test is exporting the state, wiping the namespace, and rebuilding. Most teams never run either test, and the backup post has the drill discipline for why that is a mistake.
UI and API: the dashboard versus the kubectl workflow
Argo CD ships a real web UI: application cards, sync buttons, diffs, logs, a settings surface, SSO integration, and RBAC per project. It is the reason many platform teams choose it, and it is genuinely good. The API is a first-class citizen: the argocd CLI talks to it, and everything the UI can do, the API can do, which makes automation scriptable.
Flux ships no official UI. The interfaces are kubectl, the flux CLI, and the Kubernetes API, and everything is a CRD, which means standard Kubernetes tooling, git diff, and GitOps all the way down. The Flux Web UI exists as a separate project and is improving, but it is not part of the default install. For a team that lives in a terminal, Flux is less machinery; for a team that needs a dashboard for non-Kubernetes people, Argo CD is the answer.
Repo structure: app-of-apps versus Kustomization paths
The structure that works on Argo CD is the app-of-apps: one bootstrap Application points at a directory of Application manifests, each of which can be a Helm chart, a Kustomize overlay, or plain YAML. ApplicationSets add generators for multi-cluster or multi-environment from a list or a git directory. The structure that works on Flux is a clusters directory for the platform (generated by bootstrap) plus an apps directory with one Kustomization per application, and environments expressed as separate paths or overlays.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: bootstrap
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/you/gitops
targetRevision: HEAD
path: apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: trueapiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 10m
path: ./apps
prune: true
sourceRef:
kind: GitRepository
name: flux-system
# apps/web/kustomization.yaml holds the app itselfBoth end with the same repo shape: gitops/apps/<app>/ with the manifests, and the platform layer at the root. The difference is the object that ties a path to a cluster: an Application that points at a destination, or a Kustomization that runs inside the cluster it lives on. Flux's model is more boring and more uniform; Argo CD's is more explicit about what runs where, which is exactly the trade you would expect.
The limits that bite
- Argo CD is a platform you operate. Redis, a database, an admin password to rotate, SSO to maintain, and a repo-server to upgrade. The UI hides the machinery; the machinery is still there.
- Flux has no dashboard by default. If your team wants a GUI, you are building it or running the separate Flux Web UI, which is another dependency.
- Both assume you own drift. selfHeal and force apply only work when you have told each tool which fields it may touch. Skip the ownership config and they fight your other controllers.
- Image automation on Argo is a second release train. argocd-image-updater versions, issues, and upgrades are separate from Argo CD itself. Flux's is core, but core also means it moves with the toolkit.
- Multi-cluster Argo is a bigger attack surface. one control plane with credentials to many clusters is a single high-value target; the distributed model trades that for less central visibility.
- The UI gap is not closing as fast as people hope. Argo CD's UI stays the strongest argument, and Flux's Web UI remains a separate project with its own roadmap. Test both with your actual team before betting on a roadmap.
At a glance
| Feature | Argo CD | Flux (GitOps Toolkit) |
|---|---|---|
| Architecture | Centralized control plane: server, repo-server, controller, Redis, DB | Distributed controllers per cluster, no central coordinator |
| Bootstrap | Helm chart, then app-of-apps handoff | flux bootstrap: one command, repo plus cluster committed |
| Helm | Renders charts, tracks rendered manifests | HelmRelease objects managed by helm-controller |
| Secrets | SOPS via cmp plugin or kustomize generator | Native SOPS at apply time, Age plus post-quantum in v2.9 |
| Multi-cluster | Hub-and-spoke, cluster add, central RBAC | Per-cluster controllers, path-based, no registry |
| Drift | Diff against watch-backed cache; selfHeal opt-in | Interval reconcile, applies desired state; SSA field-ignore in v2.9 |
| Image automation | argocd-image-updater, separate project | image-reflector plus image-automation controllers, core |
| Disaster recovery | Export applications, projects, secrets; reinstall | Re-bootstrap from git; restore flux-system secret |
| UI and API | Full web UI, REST API, argocd CLI | kubectl, flux CLI, Kubernetes API; no official UI |
| Repo structure | App-of-apps and ApplicationSets | Kustomizations pointing at git paths |
| License | Apache-2.0 | Apache-2.0 |
| Best for | Platform teams, many developers, central control | Operators who want less machinery and more isolation |
Architecture
- Argo CD
- Centralized control plane: server, repo-server, controller, Redis, DB
- Flux (GitOps Toolkit)
- Distributed controllers per cluster, no central coordinator
Bootstrap
- Argo CD
- Helm chart, then app-of-apps handoff
- Flux (GitOps Toolkit)
- flux bootstrap: one command, repo plus cluster committed
Helm
- Argo CD
- Renders charts, tracks rendered manifests
- Flux (GitOps Toolkit)
- HelmRelease objects managed by helm-controller
Secrets
- Argo CD
- SOPS via cmp plugin or kustomize generator
- Flux (GitOps Toolkit)
- Native SOPS at apply time, Age plus post-quantum in v2.9
Multi-cluster
- Argo CD
- Hub-and-spoke, cluster add, central RBAC
- Flux (GitOps Toolkit)
- Per-cluster controllers, path-based, no registry
Drift
- Argo CD
- Diff against watch-backed cache; selfHeal opt-in
- Flux (GitOps Toolkit)
- Interval reconcile, applies desired state; SSA field-ignore in v2.9
Image automation
- Argo CD
- argocd-image-updater, separate project
- Flux (GitOps Toolkit)
- image-reflector plus image-automation controllers, core
Disaster recovery
- Argo CD
- Export applications, projects, secrets; reinstall
- Flux (GitOps Toolkit)
- Re-bootstrap from git; restore flux-system secret
UI and API
- Argo CD
- Full web UI, REST API, argocd CLI
- Flux (GitOps Toolkit)
- kubectl, flux CLI, Kubernetes API; no official UI
Repo structure
- Argo CD
- App-of-apps and ApplicationSets
- Flux (GitOps Toolkit)
- Kustomizations pointing at git paths
License
- Argo CD
- Apache-2.0
- Flux (GitOps Toolkit)
- Apache-2.0
Best for
- Argo CD
- Platform teams, many developers, central control
- Flux (GitOps Toolkit)
- Operators who want less machinery and more isolation
Which should you pick?
- Choose Argo CD when you run a platform team, developers need a dashboard and RBAC, you manage many clusters from one place, or you already have the OpenTofu + Argo CD bootstrap in place. The UI is the feature; pay the control-plane cost.
- Choose Flux when you operate the cluster yourself, you want the fewest components to patch, you value per-cluster isolation, or you want native SOPS and core image automation. The bootstrap is genuinely one command, and the mental model is Kubernetes-native all the way down.
- Run neither when the cluster is still a single node or the workload fits Compose. The Compose vs Kubernetes verdict applies unchanged: GitOps is a platform tool, and the platform has to earn its keep first.
My honest read after running both: Argo CD's UI and RBAC are real and they matter the moment anyone other than you looks at the cluster. Flux's minimalism is real and it matters the moment you are the one doing upgrades at 2 AM. If I were building a fleet today from a clean sheet, I would start with Flux and add Argo CD only when the dashboard became the bottleneck. If the team already lives in a dashboard, start with Argo CD. Both are graduated, both are boring in the good way, and the migration between them is mostly a repo restructure plus a secret restore, which is a much smaller cost than picking wrong and living with it.
Official sources
- Argo CD documentation: https://argo-cd.readthedocs.io/
- Argo CD releases: https://github.com/argoproj/argo-cd/releases
- argocd-image-updater: https://github.com/argoproj-labs/argocd-image-updater
- Flux: https://fluxcd.io/
- Flux releases: https://github.com/fluxcd/flux2/releases
- Flux v2.9 announcement: https://fluxcd.io/blog/2026/06/flux-v2.9.0/
- Our OpenTofu + Argo CD post: https://systhoughts.com/posts/opentofu-bootstrap-kubernetes-with-argocd
- Our Compose vs Kubernetes post: https://systhoughts.com/posts/docker-compose-vs-kubernetes-self-hosted-apps
- Our Docker secrets (SOPS + age) post: https://systhoughts.com/posts/docker-secrets-arent-really-secrets
- Our backup and recovery keys post: https://systhoughts.com/posts/3-2-1-backup-rule-not-enough-self-hosted-infrastructure
- Our release-tracking workflow: https://systhoughts.com/posts/tracking-software-releases-across-forges
Are you running Argo CD, Flux, or both, and where did the control-plane versus distributed trade land for you? What did your first real drift incident teach you? Drop it in the comments.
Until next time, keep your systems thoughtful.

No comments yet