Argo CD and Flux: revisit your GitOps controller before you scale or migrate

A platform team in Pune looks after 14 Kubernetes clusters. Most of them are managed by one central Argo CD instance that the team set up in 2021. Last year the company acquired a smaller firm, and its six clusters each run Flux, bootstrapped from a Git repository per cluster. Now leadership wants one way of working before the cluster count doubles next year. The developers say they cannot live without the Argo CD screen. The acquired team says Flux is simpler to run and that it survived the Weaveworks shutdown. The security reviewer asks a question nobody has answered yet: why does the deployment tool have rights over everything in every cluster?

All three points are valid, and they pull in different directions. This post compares Argo CD and Flux as they stand today, from history and governance to sync, Helm, tenancy, footprint and upgrades. Facts come from the projects’ repositories, release pages, official docs, CNCF pages and official blogs, checked on 7 October 2026. The drill results are from my own offline run. If you are also replacing your ingress controller, the earlier post on Ingress NGINX retirement covers that side; the GitOps controller is usually what rolls such a change out.

The short version

  • Both are CNCF graduated projects under Apache 2.0, and both shipped releases in the last week: Argo CD 3.5.4 (7 October 2026 IST) and Flux 2.9.6 (1 October 2026).
  • Argo CD is an application delivery service with its own API, UI, SSO and RBAC. Flux is a set of Kubernetes controllers that rely on Kubernetes RBAC and have no UI in the core project.
  • Argo CD renders Helm charts with helm template and manages the objects itself. Flux performs real Helm releases through the Helm SDK, so helm list and Helm history keep working.
  • Argo CD usually runs as a hub that pushes to many clusters. Flux usually runs inside each cluster and pulls. Both can do the other model too.
  • After Weaveworks closed in February 2024, Flux kept its release cadence. Today its nine core maintainers come from ControlPlane, SUSE, two other companies and independents.
  • In my drill, the default Flux install had 4 controllers with resource requests and limits set. Argo CD’s install.yaml had 7 workloads with no requests or limits at all, and its application controller had a wildcard ClusterRole.

Where things stand on 7 October 2026

Argo CD

Flux

Latest release

3.5.4, 7 October 2026 IST; 3.4.10 and 3.3.15 patches the same night

2.9.6, 1 October 2026

Next

3.6.0-rc2 out; 3.6 GA planned for 3 November 2026

Next minor expected after the next Kubernetes minor

Supported lines

Three most recent minors (3.5, 3.4, 3.3)

Last three minors (2.9, 2.8, 2.7)

Licence

Apache 2.0

Apache 2.0

CNCF status

Graduated, 6 December 2022 (as part of Argo)

Graduated, 30 November 2022

Main backers

Intuit, Akuity, Red Hat, Octopus Deploy and others

ControlPlane, plus maintainers at SUSE and other firms

Built-in UI

Yes

No; separate projects provide one

Optional add-ons

Image Updater 1.3.0, argocd-agent 0.10.0 (both in argoproj-labs)

Image automation controllers, Flux Operator 0.61.0 (ControlPlane)

The timeline, from the repositories, release pages and CNCF pages:

Date

Event

7 July 2016

First Flux commit, by Peter Bourgon at Weaveworks

22 January 2018

Applatix, the company behind Argo Workflows, joins Intuit

13 March 2018

First tagged Argo CD release, v0.1.0

30 August 2018

Argo CD publicly introduced by Intuit and the Argo community

15 July 2019

Flux accepted into the CNCF

26 March 2020

Argo accepted into the CNCF at incubating level

7 April 2021

Argo CD 2.0.0

November and December 2022

Flux (30 November) and Argo (6 December) graduate

5 July 2023

Flux 2.0.0 GA

February 2024

Weaveworks ceases commercial operations; ControlPlane hires Flux core maintainers (15 February); Octopus Deploy acquires Codefresh (27 February)

6 May 2025

Argo CD 3.0.0, the first major version since 2021

24 February 2026

Flux 2.8.0 ships with Helm v4

30 June 2026

Flux 2.9.0: CLI plugins, drift ignore rules

4 August 2026

Argo CD 3.5.0: Helm 4, impersonation beta, ApplicationSet UI

Two designs for the same idea

Both tools read desired state from Git or an OCI registry, compare it with the cluster and apply the difference. They package that loop very differently.

Argo CD is a service. The API server serves the UI, CLI and webhooks, and handles SSO and RBAC. The repo server clones repositories and renders manifests from plain YAML, Kustomize, Helm or plugins. The application controller compares live state with the rendered target and runs sync operations and hooks (PreSync, Sync, PostSync). The ApplicationSet controller generates Applications from templates. A notifications controller, Redis as a cache and Dex for SSO complete the picture. The unit of work is the Application, grouped into AppProjects.

Flux is a toolkit of controllers, each with its own CRDs. source-controller fetches Git, OCI, Helm and S3-compatible sources and turns them into versioned artifacts. kustomize-controller builds and applies them. helm-controller runs Helm releases. notification-controller sends events out and receives webhooks in. Optional controllers handle image scanning, image updates and source composition (source-watcher). There is no Flux API server; the Kubernetes API is the API.

Argo CD 3.5.4

Flux 2.9.6

Main objects

Application, ApplicationSet, AppProject

GitRepository, OCIRepository, HelmRepository, Bucket, Kustomization, HelmRelease, Alert, Provider, Receiver

CRDs in default install

3

11; 15 with optional controllers

Where rendering happens

repo server

kustomize-controller and helm-controller

Access control

Argo CD RBAC on top of Kubernetes

Kubernetes RBAC only

Interfaces

Web UI, argocd CLI, gRPC and REST API

flux CLI, kubectl, third-party UIs

Headless option

Argo CD Core (core-install.yaml)

Always headless

How sync and reconcile work

In Argo CD, the controller refreshes each application on a timer. The argocd-cm reference says the reconciliation timeout is “two minutes by default with additional jitter”, and the jitter defaults to one minute; the HA guide still describes it as polling Git every three minutes. Git webhooks make it near immediate. A refresh only marks an application OutOfSync; the sync is manual unless automated sync is enabled. Even with automated sync, pruning of deleted resources and self-healing of manual cluster edits are off by default.

In Flux, every object has its own interval. source-controller publishes a new artifact when the revision changes, and kustomize-controller applies it with server-side apply. The prune field on a Kustomization is required, so you must decide garbage collection explicitly for each one. Ordering is done with dependsOn between Kustomizations or HelmReleases, and health checks with wait or explicit checks.

Default behaviour

Argo CD

Flux

Check for new commits

About 2 to 3 minutes, plus webhooks

Per object interval, plus Receivers

Apply automatically

Off until automated sync is set

Yes, at every interval

Delete removed objects

Off until prune: true

Required choice on every Kustomization

Undo manual edits

Off until selfHeal: true

Yes, at every interval

Apply method

Client-side apply; ServerSideApply=true per app or resource

Server-side apply

In short, Argo CD lets a team look before syncing, while Flux treats the commit as the gate.

Drift detection

Argo CD shows drift as a diff in the UI and CLI. Its docs list reasons an application stays OutOfSync right after a sync: mutating webhooks, controllers changing fields, charts using random functions and HPAs reordering metrics. ignoreDifferences handles those, and self-heal decides whether drift is corrected or only reported.

Flux runs a server-side apply dry run at each interval and corrects what differs. Until 2.9, this meant Flux fought any other controller that owned a field, such as an HPA setting replicas. Flux 2.9 added spec.ignore rules on Kustomizations for exactly this. For Helm, drift detection is a separate, optional setting on each HelmRelease: warn reports drift as events, and enabled also corrects it.

Helm and Kustomize

This is where many migrations hurt.

Argo CD

Flux

How Helm runs

helm template, then Argo CD applies and owns the objects

Helm SDK install, upgrade, test, rollback

helm list and history

Empty; no Helm release exists

Normal Helm releases in Helm storage

Hooks

Mapped onto Argo CD hooks; the docs call it “annotation compatibility”

Native Helm hooks; 2.9 changed the default post-render strategy to include hooks

Failure handling

Sync status and retry options

Remediation: retries, rollback or uninstall

Helm 4

Since 3.5

Since 2.8

Kustomize

kustomize build plus overrides in the Application

Built in, plus postBuild variable substitution

Kustomize exec plugins

Possible through build options or config management plugins

Not allowed, for security reasons

helm rollback runbooks work with Flux, not with Argo CD-managed charts, and charts relying on hook ordering need testing under Argo CD. Going the other way, Flux 2.9’s post-render default change is listed as breaking, so set the strategy explicitly before upgrading.

Multi-cluster: hub or per-cluster

Argo CD’s common model is a hub. One instance holds cluster credentials as secrets and pushes to each cluster. ApplicationSets with list, cluster, Git and matrix generators stamp out one Application per cluster or folder. At scale, the application controller is sharded across replicas by cluster (legacy, round-robin or the experimental consistent-hashing method). The hub needs network reach and strong credentials for every cluster. argocd-agent, still at 0.x in argoproj-labs, turns this into a pull model where agents in each cluster connect back to the hub.

Flux’s common model is one installation per cluster, each reconciling its own path in Git. Nothing outside needs credentials to it. Flux can also act as a hub, pointing a Kustomization or HelmRelease at a remote cluster through spec.kubeConfig. For very large numbers of objects, Flux shards controllers by a sharding.fluxcd.io/key label.

If your real problem is placing add-ons across hundreds of clusters by labels, Rancher Fleet (0.16.2) and Sveltos (1.16.0) target that layer. Sveltos says it extends Argo CD or Flux rather than replacing them.

Multi-tenancy, RBAC and SSO

Argo CD has its own security model. AppProjects restrict which repositories, destinations and kinds a team can use, and Casbin-style CSV policies define who can sync, delete, read logs or exec. SSO works through the bundled Dex or an existing OIDC provider. Version 3.0 tightened defaults: fine-grained policies no longer cover sub-resources, and log access needs its own permission. Version 3.5 moved impersonation to beta, so syncs and other operations can run as a chosen service account instead of the controller’s own, and made ApplicationSets in any namespace stable.

Underneath, the controller is still powerful: in the 3.5.4 install.yaml, the argocd-application-controller ClusterRole allows every verb on every resource in every API group. Teams that cannot accept this use namespace-install.yaml with per-cluster credentials, or impersonation.

Flux has no separate RBAC. By default, kustomize-controller and helm-controller are bound to cluster-admin. Multi-tenancy comes from impersonation: each tenant’s Kustomization or HelmRelease names a service account, and Kubernetes RBAC limits what it can do. The multi-tenancy guide’s lockdown sets --no-cross-namespace-refs=true, --no-remote-bases=true and --default-service-account=default, so a tenant cannot use another namespace’s sources, pull remote bases or fall back to the controller’s rights. A tenant object then looks like this:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: web
  namespace: team-web
spec:
  interval: 10m
  path: ./apps/web/overlays/prod
  prune: true
  serviceAccountName: web-deployer   # RBAC limits this account
  sourceRef:
    kind: GitRepository
    name: web
  ignore:                            # Flux 2.9: leave HPA-owned field alone
    - target: {kind: Deployment}
      paths: ["/spec/replicas"]

Argo CD gives developers a familiar access model with SSO and per-application roles; Flux gives auditors one model, Kubernetes RBAC, that they already review. Neither is safe out of the box for many tenants.

Image automation

Flux has two optional controllers for this. image-reflector-controller scans registries and picks a tag by ImagePolicy, such as the newest semver. image-automation-controller commits it back to Git at lines marked like # {"$imagepolicy": "flux-system:podinfo"}. Since 2.9 it can sign those commits with SSH keys.

Argo CD Image Updater is a separate project in argoproj-labs, now at 1.3.0. It moved from annotations to an ImageUpdater CRD in 1.0, and its own docs say it is “under active development”. It writes back either by changing the Application parameters through the Kubernetes API or by committing to Git. The first is quicker, but the running version then no longer matches Git.

Notifications and user interface

Argo CD’s notifications controller uses triggers and templates, with services such as Slack, email, Teams and webhooks. Flux’s notification-controller sends events through Providers (Slack, Teams, PagerDuty, Alertmanager, GitHub and GitLab commit status, and more) and accepts inbound webhooks through Receivers, so a push triggers reconciliation at once.

The UI is the biggest visible difference. Argo CD’s web UI shows the resource tree, diffs, logs, sync buttons and, since 3.5, ApplicationSet previews. Flux core has only the CLI; the Flux site lists other UIs: the Flux Operator web UI from ControlPlane (with OIDC SSO mapped to Kubernetes RBAC), flux9s (a terminal UI whose GA post appeared on 5 October 2026), and plugins for Headlamp, Backstage and Freelens. Weave GitOps, the free UI Weaveworks offered, is listed in the past tense. Note that the Flux Operator is AGPLv3, unlike Flux itself.

Governance after Weaveworks

Weaveworks created Flux and coined the term GitOps, so its closure in early 2024 raised a fair worry. On 15 February 2024, ControlPlane announced it had hired core maintainers Stefan Prodan and Soule Ba, and launched an enterprise distribution with hardened, FIPS-compliant images and support. The CNCF announced further corporate support in March 2024. What followed is visible in the release pages: seven minor releases, from 2.3 in May 2024 to 2.9 in June 2026, roughly one every three to five months, with Helm v4 in 2.8 and a plugin system in 2.9. The current core maintainers list has nine names: three at ControlPlane, three independent, and one each at SUSE, NexHealth and Associmates. That is healthier than a one-company project, but ControlPlane is the centre of gravity, so keep watching it.

Argo CD’s maintainer base is wider. The Argo maintainers file names Argo CD leads at Intuit and Akuity, and approvers from Red Hat, Octopus Deploy, Akuity, Intuit, BlackRock and others. In February 2024, Octopus Deploy acquired Codefresh, another Argo maintainer company. A CNCF blog post on 30 September 2026 says planning for Argo CD 4.0 has begun.

Vendor-backed options, as listed by the projects and vendors:

Argo CD

Flux

Akuity Platform, from Argo CD’s creators

ControlPlane Enterprise for Flux CD

Red Hat OpenShift GitOps

Azure AKS and Azure Arc, through the microsoft.flux extension

Octopus Deploy, which now includes Codefresh

GitLab, AWS EKS Anywhere, Giant Swarm, VMware Tanzu, Nutanix

Amazon EKS Capability for Argo CD, running in the AWS control plane

Footprint and upgrades

The default Flux install asks for 350m CPU and 256 MiB of memory across four controllers, each capped at 1 CPU and 1 GiB. Argo CD’s manifests set no requests or limits, so its pods run as BestEffort until you add them. The repo server and application controller grow with repositories and applications, so size them from your own metrics.

Argo CD ships a minor release every three months after a seven-week release candidate freeze and patches only the three latest minors, so a team that upgrades once a year falls out of support. Since 3.3, the ApplicationSet CRD is too large for client-side apply, and upgrades must use server-side apply with --force-conflicts. Flux releases at least three minors a year, following Kubernetes, supports the last three, supports Kubernetes N-2, and allows upgrading from any 2.x to any 2.x. Either way, read the notes for every minor you cross.

A decision guide

Your situation

Reasonable first choice

Many developers need to see and sync their own apps

Argo CD with SSO and AppProjects

Platform team only, everything through Git and kubectl

Flux, or Argo CD Core

Clusters behind firewalls, edge or air-gapped sites

Flux in each cluster, or argocd-agent if you accept a 0.x project

One central view of hundreds of clusters

Argo CD hub with controller sharding, or argocd-agent

Helm-heavy estate with Helm runbooks

Flux helm-controller

Auditors want only Kubernetes RBAC to review

Flux with lockdown and impersonation

Image updates written back to Git out of the box

Flux image automation

Need vendor support and SLAs

Akuity, Red Hat, Octopus or AWS for Argo CD; ControlPlane, Azure or GitLab for Flux

A practical checklist

  1. Write down why you are choosing: developer UI, cluster count, network reach, audit or support.
  2. List Helm charts using hooks, lookup or random values, and test them.
  3. Decide hub or per-cluster before you design repositories.
  4. Decide pruning and self-heal per application, and write the decision down.
  5. Remove cluster-wide rights where you can: impersonation, namespace installs or Flux lockdown.
  6. Set resource requests and limits on every controller pod, Argo CD especially.
  7. Configure webhooks or Receivers so changes do not wait for the polling timer.
  8. Add drift ignore rules for fields owned by HPAs, cert-manager or webhooks.
  9. Plan upgrades on the release cadence, and use server-side apply for Argo CD.
  10. Rehearse a restore: rebuild a cluster from Git and time it.

Common mistakes

  • Assuming Argo CD runs Helm. It renders charts; Helm commands do not see those releases.
  • Leaving Argo CD auto-sync without prune, then wondering why deleted objects stay in the cluster.
  • Fighting the HPA, in either tool, without ignore rules.
  • Using Argo CD Image Updater’s API write-back, so the cluster and Git disagree.
  • Running Flux multi-tenant without lockdown, so every tenant effectively has cluster-admin.
  • Judging Flux by Weaveworks’ fate instead of by its current releases and maintainers.

Drill: what each one installs, offline

I kept this light on purpose: no cluster, no containers, no root access. On 7 October 2026, from about 6:19 to 6:28 PM IST, on a shared Linux VM, I downloaded the official release binaries of argocd 3.5.4, flux 2.9.6, kustomize 5.8.2 and helm 4.3.0 from GitHub and get.helm.sh, and all four matched their published SHA-256 checksums. I generated Flux manifests with flux install --export, fetched the four Argo CD 3.5.4 install manifests, and counted objects with a small Python script. Image sizes are sums of compressed linux/amd64 layers from the registry manifests.

What gets installed

Flux default

Flux with image and source-watcher controllers

Argo CD install.yaml

Argo CD core-install.yaml

Argo CD ha/install.yaml

Objects

32

43

59

34

70

Deployments and StatefulSets

4 + 0

7 + 0

6 + 1

3 + 1

6 + 2

CRDs

11

15

3

3

3

ServiceAccounts

4

7

7

4

8

ClusterRoles

3

3

3

1

3

Container images

4

7

3

2

4

Requests and limits set

All

All

None

None

None

Manifest size

269,825 bytes

344,464 bytes

1,917,766 bytes

1,882,880 bytes

1,969,264 bytes

Three things stood out. First, every Argo CD component uses one quay.io/argoproj/argocd:v3.5.4 image (221.3 MB compressed), plus Dex 2.45.1 (48.1 MB) and Redis 8.2.3 (27.5 MB). The four Flux controllers are separate images, 239.5 MB together, and you can skip the ones you do not use. Second, the ApplicationSet CRD alone is about 1.39 million bytes when serialised, more than five times the 262,144-byte annotation limit, which explains the server-side apply requirement. The largest Flux CRD, HelmRelease, is about 78,000 bytes. Third, even the HA manifest runs a single application controller replica; sharding is something you configure, not something you get.

On RBAC, both defaults are broad, in different ways. Argo CD’s controller ClusterRole is a full wildcard, including non-resource URLs, and argocd-server can get, patch and delete any resource. Flux binds kustomize-controller and helm-controller to cluster-admin. Of the manifests I checked, only Argo CD’s namespace-install.yaml comes without ClusterRoles.

Same overlay, two renderings

I built a tiny repository: a base Deployment and Service, and a prod overlay setting the namespace, three replicas, a new image tag and an environment variable ${cluster_name}, which a Flux Kustomization set to prod-mumbai-1 through postBuild.substitute.

kustomize build produced the expected objects with ${cluster_name} left as literal text, which is what Argo CD’s own Kustomize rendering would also give, since it only offers environment substitution for common annotations. flux build kustomization --dry-run replaced it with prod-mumbai-1 and added kustomize.toolkit.fluxcd.io/name and namespace labels to every object. A repository that depends on Flux substitution therefore needs changes before Argo CD can use it.

Argo CD RBAC without a server

The argocd admin settings rbac commands can test a policy file offline, though they refused to start without a kubeconfig; a dummy one pointing at an unused local port was enough. The policy I tested:

p, role:web-dev, applications, get, web/*, allow
p, role:web-dev, applications, sync, web/*, allow
p, role:web-dev, logs, get, web/*, allow
g, web-developers, role:web-dev

validate said the policy is valid. For web-developers, sync of web/frontend returned Yes, sync of payments/api No, delete No, logs Yes and exec No. This is a cheap CI test for RBAC changes.

Honest limits: no live cluster, so I measured no sync or reconcile timing, memory or CPU use. Image sizes are compressed downloads, not disk use. I did not test the Argo CD Helm chart, the Flux Operator, argocd-agent, Image Updater or notifications. Counts will change with each release. No command in the drill was blocked.

What to unlearn and re-learn

  • Unlearn “GitOps tools are interchangeable”. Re-learn that Argo CD is a service with its own API and RBAC, and Flux is a set of controllers on Kubernetes RBAC.
  • Unlearn “Argo CD deploys Helm charts”. Re-learn that it renders them, and plan hooks and runbooks to match.
  • Unlearn “Flux died with Weaveworks”. Re-learn to judge a project by its recent releases and maintainer list.
  • Unlearn “the defaults are production settings”. Re-learn that both install broad cluster rights and that Argo CD sets no resource limits.

Revisit your GitOps controller before you scale or migrate

The Pune team does not need a winner; it needs a clear reason. If developers must see and act on their own applications, Argo CD’s UI and RBAC are worth running. If the platform team wants controllers inside each cluster under Kubernetes RBAC, Flux is the smaller tool. Learn how your chosen tool really syncs, renders Helm and handles drift. Unlearn the habits the other tool taught you. Re-learn tenancy and upgrades on its terms. Practise with a drill like this one and a staging cluster rebuilt from Git. Then apply what you learn to your repositories, RBAC and runbooks, before the next twenty clusters make the choice harder.

Sources

comments powered by Disqus

Releted Posts

Ingress NGINX retirement: revisit your Kubernetes ingress before you migrate

A platform team plans its Kubernetes 1.37 upgrade for the next sprint. The cluster checklist is green, except one line nobody owns: the ingress-nginx Helm chart, pinned at 4.

Read more

Vault and OpenBao: revisit your secrets manager before you renew or migrate

A platform team runs a three-node Vault Community cluster for about sixty services. In one week, a product group asks for its own isolated tenant, which in Vault means namespaces, an Enterprise feature, and finance asks whether the Enterprise quote is really needed.

Read more

Prometheus long-term storage: revisit Thanos, Mimir and VictoriaMetrics before you scale

A platform team runs two Prometheus servers per cluster as an HA pair, with 15 days of local retention. In one quarter, three requests arrive: the SRE lead wants a year of history for capacity planning, a product team adds a customer_id label to a request metric, and the auditors ask what happens to metrics when a disk dies.

Read more