Comparison

Argo CD vs Flux

Both reconcile Git into clusters and both are CNCF graduated. The choice is an operating-model choice: a control plane with a door and a dashboard, or a set of controllers you read with kubectl. This page compares what each defaults to, what each makes you build, and which one to pick for six kinds of team.

In short — Argo CD is an application-centric control plane: an API server, a repo server and a controller, with a web UI, its own RBAC and SSO, and AppProjects for tenancy. Flux is the GitOps Toolkit: source, kustomize, helm, notification and image controllers that install themselves from your repository and expose everything as Kubernetes objects and events. Same loop, different room to secure and different team it suits.
Argo CD vs Flux: summary by dimension
DimensionArgo CDFlux
ShapeAPI server + repo server + application controller (plus ApplicationSet, notifications)Composable controllers: source, kustomize, helm, notification, image-reflector, image-automation
Latest release (Sep 2026)v3.5.3v2.9.5
Unit of workApplication CRD (repo + path + destination); ApplicationSet generatorsGitRepository / OCIRepository / HelmRepository source + Kustomization or HelmRelease
UIFirst-party web UI and gRPC/REST APINone first-party; Flux Operator UI, Capacitor, Headlamp plugin and others
Sync defaultsPoll every 120s + 60s jitter; auto-sync, prune and self-heal all opt-inInterval is a required field per source; prune is explicit per Kustomization
Tenancy boundaryAppProject (repos, destinations, resource kinds, project roles); optional sync impersonation per destination (beta, off by default)Namespaces + service-account impersonation; lockdown flags on the controllers
Helmhelm template only; releases invisible to helm lshelm-controller performs real installs/upgrades with rollback and tests
SecretsNo native decryption; destination-cluster operators recommendedNative SOPS decryption in kustomize-controller (age, PGP, cloud KMS, Vault)
Image automationArgo CD Image Updater (separate argoproj-labs project, active development)Built-in image-reflector and image-automation controllers, commits to Git
Progressive deliveryArgo Rollouts (separate controller and CRDs)Flagger (part of the Flux family)
Published Jun 1, 2025·Updated ·By SecOpsLog

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.

the same service, declared in each engine
# Argo CD: one object, enforcement opted in explicitly
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: shop
namespace: argocd
spec:
project: team-shop # AppProject bounds repos, destinations, kinds
source:
repoURL: https://gitlab.example.com/platform/deploy.git
targetRevision: main
path: apps/shop/prod
destination:
server: https://kubernetes.default.svc
namespace: shop
syncPolicy:
automated:
prune: true # off unless you say so
selfHeal: true # off unless you say so
---
# Flux: a source and a reconciler, enforcement is the default
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: deploy
namespace: shop
spec:
interval: 1m # required: how often to fetch
url: https://gitlab.example.com/platform/deploy.git
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: shop
namespace: shop
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: deploy
path: ./apps/shop/prod
prune: true # explicit per reconciler
serviceAccountName: 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.

Both start open; they close differently
Argo CD is open because the default AppProject allows everything and admin bypasses RBAC. Flux is open because the controllers are cluster-admin and impersonation is opt-in. Neither is safe on day one. Argo CD closes with objects (projects, RBAC policy, SSO); Flux closes with controller flags and per-tenant service accounts. Pick the one your platform team will actually keep tight.

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.

two engines, one stuck deploy (representative output)
argocd app get shop
Name: argocd/shop
Project: team-shop
Sync Policy: Automated (Prune)
Sync Status: OutOfSync from main (9f2a1c3)
Health Status: Degraded
GROUP KIND NAMESPACE NAME STATUS HEALTH MESSAGE
apps Deployment shop api OutOfSync Degraded ...
flux get kustomizations -n shop
NAME REVISION SUSPENDED READY MESSAGE
shop main@sha1:9f2a1c3 False False Deployment/shop/api dry-run failed: ...
flux events -n shop --for Kustomization/shop
LAST SEEN TYPE REASON OBJECT MESSAGE
2m 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 situationChooseWhyPrimary trade-off
Many product teams deploying to shared clusters, self-service expected, a security team that wants one consoleArgo CDAppProjects, SSO groups and the UI are built for humans sharing a control plane; ApplicationSet generators stamp out per-team appsOne 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 loginFluxNothing to log into, bootstrap is the disaster plan, status is events you already collectYou 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 matterFluxhelm-controller creates real releases with rollback, tests and remediation; Argo CD only templatesHelm failures now surface as HelmRelease conditions you have to watch
Encrypted secrets committed to Git (SOPS) as the standardFluxNative decryption in kustomize-controller with age, PGP, KMS or Vault keysKey custody lives in the cluster; rotate deliberately
Automated image promotion without a CI job writing YAMLFluxImage automation is a supported component that commits to Git; the Argo CD equivalent is a separate project still in active developmentDeploy key needs write access to the repo
Regulated shop that wants sync history, per-app RBAC and an audit trail in one productArgo CDApplication history, project roles and SSO-backed RBAC are first-partyHA 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.

Found a technical issue on this page? Report it with the tool version you used and the behavior you saw. How resources are maintained.

Go deeper
Hands-on courses on Argo CD & Flux