If you want to learn how either engine works before choosing, the Argo CD vs Flux lesson walks the mechanics with commands: bootstrap, drift and self-heal, blast radius, tenancy lockdown and commit signing. This page assumes you know the loop and need to pick a machine.
What you are actually installing
Argo CD is three services. The API server "is a gRPC/REST server which exposes the API consumed by the Web UI, CLI, and CI/CD systems" and owns authentication, RBAC enforcement, and the repository and cluster credentials, "stored as K8s secrets". The repository server keeps a local cache of Git and renders manifests. The application controller "continuously monitors running applications and compares the current, live state against the desired target state". Since v2.3 the ApplicationSet controller ships with it. You get one control plane, one login, one screen for the whole fleet.
Flux is the GitOps Toolkit: source-controller fetches Git, OCI, Helm repositories and buckets; kustomize-controller and helm-controller apply what the sources produce; notification-controller turns status into events and alerts; image-reflector and image-automation are optional extras. There is no API server and no UI in the box. The bootstrap command "deploys the Flux controllers on Kubernetes cluster(s) and configures the controllers to sync the cluster(s) state from a Git repository", pushes the Flux manifests into that repository, and "configures Flux to update itself from Git". After that, per the docs, "any operation on the cluster(s) (including Flux upgrades) can be done via Git push, without the need to connect to the Kubernetes API".
That difference is the whole comparison in miniature. Argo CD concentrates capability and credentials in one place you must secure and operate; Flux spreads them into ordinary controllers and makes Git the only door. Argo CD v3.5.3 and Flux v2.9.5 were the current releases on the verification date.
Sync behaviour and the defaults that surprise people
Argo CD polls Git on a schedule set by timeout.reconciliation, which "defaults to 120s with added jitter of 60s for a maximum period of 3 minutes". Automated sync is opt-in per Application, and two further behaviours are off by default even when auto-sync is on: "automated sync will not delete resources when Argo CD detects the resource is no longer defined in Git" until you enable pruning, and "changes that are made to the live cluster will not trigger automated sync" until you enable self-heal. A new Argo CD Application therefore reports drift on a dashboard and waits for a human unless you asked it to act. That is a safe default and a common source of "GitOps is not enforcing anything" surprise.
Flux has no global poll. Every source declares its own interval, ".spec.interval is a required field that specifies the interval at which the Git repository must be fetched", and every Kustomization declares whether it prunes. There is no separate self-heal switch because reconciliation is the only mode: a Kustomization applies its manifests on every interval and reverts what differs, except fields excluded with .spec.ignore rules (added in Flux 2.9). The consequence is that Flux enforces by construction and Argo CD enforces by configuration.
# Argo CD: one object, enforcement opted in explicitlyapiVersion: argoproj.io/v1alpha1kind: Applicationmetadata:name: shopnamespace: argocdspec:project: team-shop # AppProject bounds repos, destinations, kindssource:repoURL: https://gitlab.example.com/platform/deploy.gittargetRevision: mainpath: apps/shop/proddestination:server: https://kubernetes.default.svcnamespace: shopsyncPolicy:automated:prune: true # off unless you say soselfHeal: true # off unless you say so---# Flux: a source and a reconciler, enforcement is the defaultapiVersion: source.toolkit.fluxcd.io/v1kind: GitRepositorymetadata:name: deploynamespace: shopspec:interval: 1m # required: how often to fetchurl: https://gitlab.example.com/platform/deploy.gitref:branch: main---apiVersion: kustomize.toolkit.fluxcd.io/v1kind: Kustomizationmetadata:name: shopnamespace: shopspec:interval: 5msourceRef:kind: GitRepositoryname: deploypath: ./apps/shop/prodprune: true # explicit per reconcilerserviceAccountName: shop-deployer # the tenant boundary (see below)
Read the two manifests as a statement of where each engine puts the boundary. Argo CD binds the source, destination and policy into one object that lives in the argocd namespace and is governed by an AppProject. Flux separates the source from the reconciler, keeps both in the tenant's namespace, and makes the reconciler carry its own identity.
Tenancy, RBAC and who can log in
Argo CD has an opinion about people. It ships one built-in admin, which the docs say to use "only for initial configuration and then switch to local users or configure SSO integration"; SSO is either the bundled Dex (OIDC, SAML, LDAP, GitHub and more) or an existing OIDC provider. RBAC is a Casbin policy in the argocd-rbac-cm ConfigMap with two built-in roles, role:readonly and role:admin, and "all authenticated users get at least the permissions granted by the default policies". Tenancy is the AppProject: it restricts source repositories, destination clusters and namespaces, and resource kinds, and defines project roles. The catch is the default project, which "permits deployments from any source repo, to any cluster, and all resource Kinds" and "can be modified, but not deleted". A fresh install is wide open until someone writes projects. Syncs also run with the privileges of the Argo CD control plane by default; sync impersonation, which maps each AppProject destination to a service account, is documented as beta and is disabled by default.
Flux has no users at all; the people model is Git's. Its tenancy model is Kubernetes RBAC plus impersonation: a Kustomization or HelmRelease names a serviceAccountName and the controller applies as that account. The trap, documented plainly, is the default: controllers run under a service account "which has the cluster-admin role bound to it", so a reconciler that omits the field applies as cluster-admin. The lockdown is three controller flags: --no-cross-namespace-refs=true so "a tenant can't use another tenant's sources or subscribe to their events", --no-remote-bases=true so only Flux sources can affect cluster state, and --default-service-account so a forgotten field falls back to a low-privilege account instead of cluster-admin.
Helm, Kustomize and secrets
This is a real behavioural difference, not a feature-list one. Argo CD uses Helm "only to inflate charts with helm template"; "the lifecycle of the application is handled by Argo CD instead of Helm", so a chart deployed by Argo CD does not appear in helm ls, Helm hooks are mapped onto PreSync and PostSync annotations, and test hooks are not supported. Flux's helm-controller does the opposite: it performs genuine install and upgrade operations against a HelmRelease, runs Helm tests, and offers "automated remediation (rollback, uninstall, retry) on failed Helm install, upgrade or test actions". If your organisation's runbooks, tooling or vendors assume Helm releases exist in the cluster, Flux preserves that; Argo CD replaces it with its own sync history.
Secrets follow the same split. Argo CD's documentation lists two ways to populate secrets and "strongly recommends" the destination-cluster approach (Sealed Secrets, External Secrets Operator, the Secrets Store CSI driver, Vault Secrets Operator) over injecting secrets during manifest generation, because plugin-generated manifests, "along with the injected secrets", are cached in Redis and exposed through the repo-server API. Flux's kustomize-controller decrypts SOPS-encrypted manifests natively, with keys from age, PGP, AWS KMS, Azure Key Vault, GCP KMS or HashiCorp Vault, referenced through spec.decryption. In practice: encrypted-secrets-in-Git is a first-class Flux workflow and an add-on pattern in Argo CD, while operator-based secrets work identically on both.
Image automation and progressive delivery
Flux's image automation is built in but optional: an ImageRepository scans the registry, an ImagePolicy picks the tag, and an ImageUpdateAutomation will "checkout a branch, commit and push the changes to the remote Git repository", guided by markers such as {"$imagepolicy": "flux-system:podinfo"} in your YAML. It is enabled with --components-extra=image-reflector-controller,image-automation-controller at bootstrap and needs a deploy key with write access. Argo CD Image Updater is a separate argoproj-labs project that its own docs describe as "under active development", to be tried "on non-critical environments", limited to applications rendered with Kustomize, Helm or a config management plugin, with write-back through the Argo CD API or Git.
Progressive delivery is an add-on on both sides. Argo Rollouts is "a Kubernetes controller and set of CRDs" for blue-green, canary, analysis and experiments; Flagger is "a progressive delivery Kubernetes operator" in the Flux family with canary, A/B and blue-green mirroring across Istio, Linkerd, Kuma, NGINX, Contour and others. Neither engine does traffic shifting itself; you are choosing which sibling project to run next to it.
When it breaks: what you look at
The day-two question is not which tool has more features but where the truth is when a deploy is stuck. In Argo CD the truth is the Application status, visible in the UI and reproducible from the CLI; in Flux it is a set of object conditions and Kubernetes events, which the notification-controller can forward to Slack, Teams or Grafana and the flux CLI reads directly. Both expose Prometheus metrics; Flux publishes Grafana dashboards for its controllers.
argocd app get shopName: argocd/shopProject: team-shopSync Policy: Automated (Prune)Sync Status: OutOfSync from main (9f2a1c3)Health Status: DegradedGROUP KIND NAMESPACE NAME STATUS HEALTH MESSAGEapps Deployment shop api OutOfSync Degraded ...flux get kustomizations -n shopNAME REVISION SUSPENDED READY MESSAGEshop main@sha1:9f2a1c3 False False Deployment/shop/api dry-run failed: ...flux events -n shop --for Kustomization/shopLAST SEEN TYPE REASON OBJECT MESSAGE2m Warning ReconciliationFailed Kustomization/shop Deployment/shop/api dry-run failed ...The shape matters more than the exact text. Argo CD gives you one summary object with sync and health separated, and the same picture in the UI; Flux gives you a status per reconciler and the event that explains it. In our judgement, teams that already live in kubectl and their alerting stack will find Flux's model natural, and teams that want an on-call engineer to open one page at 3am and see red or green will find Argo CD's model natural. Neither sees more.
Bootstrap, backup and recovery
Flux's recovery story is its install story. Bootstrap "is idempotent, it's safe to run the command as many times as you want", performs an upgrade if controllers are present, and everything Flux needs to rebuild a cluster is the Git repository it committed itself into. Argo CD keeps state that is not in your application repositories: cluster credentials, repository credentials, projects and settings live in the argocd namespace. The documented recovery is argocd admin export and argocd admin import, run with the image of the same Argo CD version, and the docs warn that the export "will not fail if you run it in the wrong namespace". You back up Argo CD; you re-bootstrap Flux.
Scale follows the same pattern. Argo CD ships HA manifests that run Redis in HA mode and require "at least three different nodes due to pod anti-affinity", with the application controller sharded across replicas by cluster. Flux has no Redis-style shared cache to make highly available; scaling is controller resources and how you split work across Kustomizations, which is an inference from the architecture rather than a sizing guide.
Which fits which team
Which engine, by team
| Team and situation | Choose | Why | Primary trade-off |
|---|---|---|---|
| Many product teams deploying to shared clusters, self-service expected, a security team that wants one console | Argo CD | AppProjects, SSO groups and the UI are built for humans sharing a control plane; ApplicationSet generators stamp out per-team apps | One well-guarded door: the argocd namespace holds every cluster credential, so SSO and RBAC are not optional |
| Small platform team, kubectl-native, alerting already in Prometheus or Slack, no appetite for another login | Flux | Nothing to log into, bootstrap is the disaster plan, status is events you already collect | You build the dashboard (or adopt one from the ecosystem) and you must apply the tenancy flags yourself |
| Helm-heavy estate where runbooks, vendors or helm ls semantics matter | Flux | helm-controller creates real releases with rollback, tests and remediation; Argo CD only templates | Helm failures now surface as HelmRelease conditions you have to watch |
| Encrypted secrets committed to Git (SOPS) as the standard | Flux | Native decryption in kustomize-controller with age, PGP, KMS or Vault keys | Key custody lives in the cluster; rotate deliberately |
| Automated image promotion without a CI job writing YAML | Flux | Image automation is a supported component that commits to Git; the Argo CD equivalent is a separate project still in active development | Deploy key needs write access to the repo |
| Regulated shop that wants sync history, per-app RBAC and an audit trail in one product | Argo CD | Application history, project roles and SSO-backed RBAC are first-party | HA needs three nodes and Redis; back up the argocd namespace on a schedule |
The trade-offs we would weigh
A central control plane gives you one UI, one API, one RBAC model and one place to see the fleet. It is also one place that holds every cluster credential and must be hardened, backed up and made highly available: Argo CD is a service you run, where Flux is a set of controllers you upgrade with a Git push. Whoever is on call for the deploy system itself should weigh this first.
Enforcement by default cuts both ways. Flux corrects drift on every interval without a flag, which means a bad commit is applied just as faithfully and the only brake is Git review. Argo CD keeps prune and self-heal opt-in, so a first rollout cannot delete anything by accident, and the matching cost is teams who believe they have GitOps enforcement when they have a dashboard. Anyone whose audit story depends on the cluster matching Git should decide which of those two failure modes they would rather explain.
Helm fidelity: Flux gives you real releases, rollback and tests, and with them Helm's own failure modes; Argo CD gives you one sync model for everything, and with it a helm ls that shows nothing and chart hooks that are approximated. Teams whose vendor charts rely on hooks and tests will feel this first.
Secrets: SOPS in Git works out of the box on Flux, with decryption keys that then live in the cluster. Argo CD's documentation pushes you toward operator-based secrets, which is the safer pattern on both engines, and treats in-Git encryption as an add-on with documented caching risks. Anyone who wants secrets reviewed in pull requests is choosing Flux's model whether or not they name it.
Performance. Neither project's documentation benchmarks it against the other, and none was run for this page. Scaling advice on both sides is about sharding work (Argo CD controller shards, Flux Kustomization splits), not raw speed.
Our pick, by situation
For a platform team serving many product teams on shared clusters, we would run Argo CD, and we would not let it exist for a day without SSO, a non-default project per team, and a scheduled export of the argocd namespace. The evidence for that recommendation is the projects and RBAC documentation: the tenancy model is built for exactly this, and the default project is the reason the hardening cannot wait.
For a small team that already lives in kubectl, Prometheus and Slack, we would run Flux with the three lockdown flags applied at bootstrap and a service account on every Kustomization, and we would adopt one of the ecosystem UIs only when someone actually asks for a screen. That is a recommendation informed by the multi-tenancy documentation, which is unusually honest about the cluster-admin default; it is not a claim that Flux is simpler to operate, only that it is smaller to operate.
If the estate depends on real Helm releases (helm ls, rollback, tests) or secrets-in-Git is policy, Flux covers those mechanics first-party at any team size. If a compliance function needs per-application history and human RBAC in one product, Argo CD covers them first-party at any team size. Both are production-grade; the failure mode to avoid is choosing the dashboard and forgetting to close the door, or choosing the controllers and forgetting to build the view.