Executive Snapshot

Dimension Flux Argo CD
Project status CNCF graduated CNCF graduated
Core abstraction A set of controllers plus GitRepository, Kustomization, HelmRelease CRs A single Application CR reconciled by one application controller
Web UI None first-party; Azure Portal GitOps blade covers the AKS extension First-class, and the main reason teams pick it
AKS install First-party cluster extension: az k8s-extension create --extension-type microsoft.flux Vanilla Helm chart or manifests you own end to end
Helm semantics Real helm install and helm upgrade through the Helm SDK; a release exists in the cluster helm template output applied as plain manifests; no Helm release object
Ordering dependsOn between Kustomizations and HelmReleases, cluster-wide Sync waves and hooks inside one Application; app-of-apps across them
Secrets Native SOPS decryption in the controller Needs a plugin or an external operator
Multi-tenancy Namespace plus service-account impersonation, cross-namespace refs disabled AppProjects plus Casbin RBAC and SSO groups
Multi-cluster One Flux install per cluster, pulling its own path Central control plane with registered clusters, or one per cluster
Upgrade burden Azure-managed if you use the extension Yours, including the chart, CRDs, and the ingress in front of the UI
Best for Platform teams standardising AKS fleets under Azure Policy Teams that need visual day-2 operations and self-service

Both tools are mature, both are safe production choices, and the wrong one will still deploy your workloads. The differences that matter are operational, not functional — who looks at the cluster state, how Helm behaves when something fails, and who owns the upgrade.


TL;DR

  • The GitOps model is identical; the operator experience is not. Both agents run in-cluster and pull from Git. Argo CD gives you a UI and a synchronous mental model; Flux gives you a set of composable CRs and no screen to look at.
  • On AKS, Flux is the first-party path. Microsoft ships it as the microsoft.flux cluster extension, drives it through az k8s-configuration flux, and can enforce it fleet-wide with Azure Policy. Argo CD you install and own yourself.
  • The Helm difference is behavioural, not cosmetic. Argo CD runs helm template and applies the manifests — helm ls will show nothing. Flux performs an actual Helm release with real hooks, tests, and rollback. This changes how failures behave.
  • Argo CD's UI is its biggest edge and it is a genuine operational asset: live diff, resource tree, sync history, and per-app rollback without touching a terminal.
  • Secrets favour Flux slightly. SOPS decryption is built into kustomize-controller. Argo CD needs a config management plugin or an external operator. Both work fine with External Secrets Operator against Key Vault.
  • Ordering works differently. Flux orders across objects with dependsOn; Argo CD's sync waves only order resources inside one Application.
  • Pick on team shape. Central platform team standardising many clusters: Flux. Many product teams who need to see and drive their own deploys: Argo CD.

Introduction

Every AKS platform team eventually reaches the same fork. The CI pipeline that has been running kubectl apply or helm upgrade from a GitHub Actions runner works, but it has accumulated the usual problems: the runner holds cluster-admin credentials, nobody can tell you what is actually deployed without querying the cluster, and a manual kubectl edit during an incident silently becomes permanent state.

GitOps fixes all three by inverting the direction of the deployment. Instead of a pipeline pushing into the cluster, an agent inside the cluster pulls the desired state from Git and continuously reconciles towards it. Flux and Argo CD both implement that model, both are CNCF graduated projects, and both will do the job. Choosing between them on feature checklists is unproductive — the checklists converged years ago.

What has not converged is how each one behaves on day two: what happens when a Helm upgrade fails at 2am, who can see the diff between Git and the cluster without shell access, how a platform team enforces the same configuration across thirty AKS clusters, and who is responsible when the controller itself needs upgrading. This article compares those things, on AKS specifically, and ends with working YAML for the same application deployed both ways.


The GitOps Model: Pull-Based Reconciliation vs Push CI/CD

The distinction is worth stating precisely because it drives most of the differences that follow.

In a push pipeline, the CI system is the actor. It authenticates to the cluster, applies manifests, and then forgets. The cluster's state after the apply is whatever the last successful run produced, plus whatever anyone has done manually since. Credentials for the cluster live in the CI system, which means your build platform is inside your cluster's trust boundary.

In a pull pipeline, an in-cluster agent is the actor. It reads Git, compares against live state, and applies the difference — on a loop, forever. The cluster's credentials never leave the cluster, and CI's job shrinks to building the image and writing a new tag into a config repository.

flowchart LR
    A[Developer commits code] --> B[CI builds and pushes image]
    B --> C[CI updates image tag in config repo]
    C --> D[Git config repository]
    D --> E[In-cluster agent polls Git]
    E --> F[Compare desired vs live state]
    F --> G[Apply the difference]
    G --> H[AKS cluster]
    H --> F
    I[Manual kubectl edit] --> H
    F --> J[Drift detected and reverted]

The loop back from the cluster into the comparison step is the part push pipelines do not have, and it is what makes drift correction possible at all. Both Flux and Argo CD implement that loop. They just expose it differently.


How Flux Is Built

Flux is not one binary. It is a set of single-purpose controllers, each owning one custom resource, composed into a delivery pipeline:

  • source-controller fetches artifacts. It owns GitRepository, OCIRepository, HelmRepository, and Bucket, verifies them, and republishes them as tarballs other controllers consume.
  • kustomize-controller takes a source and applies it. It owns Kustomization, which points at a path in a source, builds the Kustomize overlay, and applies the result with server-side apply.
  • helm-controller owns HelmRelease and performs genuine Helm operations.
  • notification-controller handles inbound webhooks and outbound alerts to Teams, Slack, or Event Grid.
  • image-reflector-controller and image-automation-controller scan a registry for new tags and commit the updated tag back to Git — image-based continuous delivery without a CI step.

The composability is the point. A Kustomization can dependsOn another Kustomization, so you can express "install cert-manager and wait for it to be healthy before applying anything that needs a Certificate" as a declarative graph across independent objects. This is genuinely cluster-wide ordering, not ordering within a bundle.

Multi-tenancy in Flux is built from Kubernetes primitives rather than an application-layer permission model. Each tenant gets a namespace, a ServiceAccount, and a Kustomization with spec.serviceAccountName set — the controller then impersonates that account when applying, so a tenant's Git repository can never apply anything their service account is not permitted to create. The lockdown is completed with controller flags:

patches:
  - patch: |
      - op: add
        path: /spec/template/spec/containers/0/args/-
        value: --no-cross-namespace-refs=true
    target:
      kind: Deployment
      name: "(kustomize-controller|helm-controller|notification-controller|image-reflector-controller|image-automation-controller)"
  - patch: |
      - op: add
        path: /spec/template/spec/containers/0/args/-
        value: --default-service-account=default
    target:
      kind: Deployment
      name: "(kustomize-controller|helm-controller)"

--no-cross-namespace-refs=true stops a tenant referencing another tenant's sources or secrets. --default-service-account stops a Kustomization that omits serviceAccountName from silently inheriting the controller's own cluster-admin-level permissions, which is the single most common Flux multi-tenancy mistake.


How Argo CD Is Built

Argo CD centres on one resource: the Application. It names a source (repo, revision, path), a destination (cluster and namespace), and a sync policy. An application controller reconciles it, a repo-server renders manifests, and an API server backs the UI and CLI.

That single-object model is why Argo CD can present a coherent screen. Every application has a health status and a sync status, a resource tree showing what it produced, a live diff against Git, and a history of syncs you can roll back to. For an on-call engineer with no kubectl access, this is transformative — "which app is out of sync and what changed" is one click rather than a jq expression.

ApplicationSets solve the scaling problem. Rather than writing one Application per app per cluster, an ApplicationSet uses a generator — clusters, Git directories, pull requests, a list, or a merge of several — to produce Applications from a template. A cluster generator plus a label selector deploys the same app to every production cluster and removes it automatically when a cluster is deregistered.

Sync waves and hooks control ordering. argocd.argoproj.io/sync-wave: "-1" on a namespace or CRD makes it apply before wave 0; hooks annotated PreSync, Sync, PostSync, or SyncFail run Jobs at defined points, with the whole sync failing if a hook fails. The critical limitation: waves order resources within a single Application only. They do not coordinate across separately auto-synced Applications. Cross-application ordering means the app-of-apps pattern, and it is noticeably less crisp than Flux's dependsOn.


The AKS Angle: One Is First-Party, One Is Not

This is where the comparison stops being generic.

Microsoft ships Flux as a supported AKS cluster extension. You do not install Flux yourself; you ask Azure for it:

az extension add --name k8s-configuration
az extension add --name k8s-extension

az k8s-configuration flux create \
  --resource-group aks-platform-rg \
  --cluster-name aks-prod-weu \
  --cluster-type managedClusters \
  --name platform-baseline \
  --namespace flux-system \
  --scope cluster \
  --url https://github.com/contoso/aks-platform-config \
  --branch main \
  --kustomization name=infra path=./infrastructure prune=true \
  --kustomization name=apps path=./apps/prod prune=true dependsOn=["infra"]

The first invocation installs the microsoft.flux extension automatically if it is not present. Practical consequences:

  • Microsoft owns the upgrade. The extension defaults to auto-upgrading minor versions, so controller patching stops being your problem. The trade is that the extension version trails upstream Flux, so a feature announced in a Flux release is not available to you the same week.
  • Azure Policy can enforce it fleet-wide. Built-in policy definitions deploy a Flux configuration to any AKS or Arc-enabled cluster in scope. That is a governance capability with no Argo CD equivalent — a new cluster arrives already reconciling the platform baseline before anyone touches it.
  • The Azure Portal gives Flux a screen. The GitOps blade shows configuration and compliance state per cluster. It is not an Argo CD-class UI, but it removes the "Flux has no visibility at all" objection for the extension-managed case.
  • The same command works for Arc. Swap --cluster-type managedClusters for connectedClusters and the identical model applies to on-premises or other-cloud clusters. For hybrid estates this is the strongest single argument in the comparison.
  • Recent extension versions enable the multi-tenancy lockdown by default, which means cross-namespace source references are blocked out of the box. If an existing manifest set suddenly fails to reconcile after an extension upgrade, check this first, and confirm the current default and opt-out setting in Microsoft's GitOps documentation for your extension version.

Argo CD on AKS is a normal Helm install. You own the chart version, the CRDs, the upgrade path, the ingress and TLS in front of the UI, the SSO configuration, and the backup of the Argo CD namespace. Partner-managed offerings exist in the Azure Marketplace, but there is no first-party Azure cluster extension. That is not a defect — plenty of teams want the control — but it is real recurring work that the Flux extension removes.


Head to Head

Day-2 visibility

Argo CD wins this outright, and for many teams it decides the whole comparison. The resource tree, live-vs-desired diff, event stream, and one-click rollback are the difference between an incident that needs a platform engineer and one a service owner can handle.

Flux's answer is telemetry, not a screen: controller status conditions, Prometheus metrics, and notification-controller alerts into Teams or Event Grid. That is arguably the more correct design for an unattended platform — you should be alerted, not watching — but it requires you to build the alerting before you need it. On AKS the Portal GitOps blade narrows the gap for extension-managed configurations.

Helm handling: the gotcha that actually bites

This is the most consequential behavioural difference between the two tools.

Argo CD uses Helm as a template engine only. It runs helm template with pinned parameters and applies the rendered manifests directly. No Helm release is created, so helm ls in that namespace returns nothing, helm rollback does not exist, and Helm's own lifecycle hooks are not executed as Helm hooks — Argo CD converts them into its own hook model. Helm test hooks are skipped by default. Rollback is an Argo CD concept, driven from Argo's sync history.

Flux uses Helm as Helm. helm-controller performs real install and upgrade actions through the Helm SDK. A release object exists in the cluster, helm ls shows it, hooks run, helm test runs if enabled, and failed upgrades can be remediated by an actual Helm rollback.

Three practical consequences:

  1. Charts that depend on Helm hook semantics — pre-upgrade migration Jobs, CRD install hooks, cleanup hooks — behave differently under Argo CD. Most charts are fine; the ones that are not fail in confusing, chart-specific ways.
  2. The lookup template function does not work under Argo CD. The repo-server renders without cluster access, so any chart using lookup to read live cluster state will render empty values.
  3. Recovery is different. Flux can automatically retry or roll back a failed upgrade using Helm's own machinery. Argo CD recovers by syncing to a previous Git revision or a previous history entry, which is a different and arguably cleaner model — but if your chart's failure mode assumes a Helm rollback will clean up after it, that assumption is now wrong.

Neither is broken. But if your organisation has a large estate of third-party charts with hook-heavy install logic, Flux's semantics will surprise you less.

Secret management

Flux has native SOPS support in kustomize-controller. Encrypted files in Git are decrypted at apply time, with the key material coming from a Secret or from an Azure workload identity via serviceAccountName:

  decryption:
    provider: sops
    serviceAccountName: sops-identity
    secretRef:
      name: sops-keys-and-credentials

Note the tenancy constraint: a SOPS-encrypted Secret must live in the same namespace as the Kustomization that references it, because cross-namespace secret references are not supported.

Argo CD has no built-in decryption. You add a config management plugin such as KSOPS or helm-secrets, which means maintaining a sidecar container on the repo-server and keeping it working across upgrades. It is a well-trodden path but it is extra surface area.

Both tools are equal on the operator-based patterns, which is where most Azure shops end up anyway. Sealed Secrets works identically under either. External Secrets Operator pulling from Azure Key Vault via workload identity works identically under either, and is the pattern to prefer on AKS: the encrypted material never enters Git at all, rotation happens in Key Vault, and neither GitOps agent needs decryption capability. If you are starting fresh on AKS, choose External Secrets first and treat SOPS support as a tiebreaker rather than a deciding factor.

RBAC and SSO

Argo CD has a full application-layer permission model. Casbin policies in argocd-rbac-cm map SSO groups to roles scoped to projects and actions:

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  policy.default: role:readonly
  scopes: '[groups, email]'
  policy.csv: |
    p, role:payments-deployer, applications, sync, payments/*, allow
    p, role:payments-deployer, applications, get, payments/*, allow
    p, role:payments-deployer, applications, override, payments/*, allow
    g, "8f2c1e4a-0b7d-4a19-9c3e-6f1b2d5a7c40", role:payments-deployer

Wire that to Entra ID as an OIDC provider and the group claim — Entra emits group object IDs by default — grants a product team the ability to sync and roll back their own applications and nothing else. AppProject adds a second layer, restricting which repositories, destination clusters, namespaces, and resource kinds a team's Applications may use at all.

Flux has no application-layer RBAC because it has no application layer. Authorisation is Kubernetes RBAC via service-account impersonation, plus your Git provider's branch protection and CODEOWNERS. This is fewer moving parts and one less identity system to reason about, and it is genuinely elegant. It is also useless to anyone who does not have kubectl access, which is precisely the group Argo CD's model is designed to serve.

Multi-cluster

Argo CD's default is a central control plane: one Argo CD installation with many clusters registered, and ApplicationSets fanning applications across them. One pane of glass, one place to grant access, one blast radius. The control plane needs credentials to every managed cluster, so it becomes a high-value target and a single point of failure that deserves its own availability planning.

Flux's default is one install per cluster, each pulling its own path from a shared repository. There is no central component, so there is no central failure. A cluster that loses connectivity to everything except Git keeps reconciling. The cost is that "what is deployed everywhere" is a question you answer with tooling rather than a screen — and on AKS, Azure Policy plus the Portal's compliance view answers a useful subset of it.

You can run Argo CD per-cluster too, and many teams do. But then you are running many UIs, many SSO configurations, and many upgrades.

Drift behaviour and self-heal

Both correct drift. The defaults differ, and the defaults are where incidents come from.

Argo CD's automated sync is opt-in on both axes: prune and selfHeal both default to false. An Application with automated: {} and nothing else will deploy new Git commits but will neither delete removed resources nor revert a manual kubectl edit. That is a reasonable default and a frequent surprise.

Flux's Kustomization reconciles on spec.interval and applies with server-side apply, so field-level drift is corrected on the next pass whether you asked for it or not. Pruning is opt-in via spec.prune: true. For Helm, drift correction is opt-in per release via spec.driftDetection.mode: enabled.

The operational difference: Argo CD's self-heal is visible — the UI shows OutOfSync, then the sync event. Flux's correction is silent unless you have notifications configured. More than one team has spent an afternoon debugging a config change that "kept reverting" before realising the platform was working exactly as designed.

Upgrade and maintenance burden

With the AKS extension, Flux's upgrade burden is close to zero: Azure patches the controllers, and the extension's auto-upgrade for minor versions is on by default. Verify that in a non-production cluster first — auto-upgrade means a controller version can change without a change request in your system.

Argo CD's burden is ordinary but non-trivial: chart upgrades, CRD updates that occasionally require care, the ingress and certificate in front of the UI, the SSO integration, and a backup strategy for the Argo CD namespace so a lost control plane does not mean re-registering every cluster by hand. Budget it as a component you operate, because that is what it is.


The Same App, Both Ways

An orders-api service, Kustomize overlays in a config repository, deployed to the orders namespace on a production AKS cluster.

Flux:

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: orders-api
  namespace: flux-system
spec:
  interval: 1m
  url: https://github.com/contoso/orders-api-config
  ref:
    branch: main
  secretRef:
    name: orders-api-git-auth
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: orders-api
  namespace: flux-system
spec:
  interval: 10m
  retryInterval: 1m
  timeout: 5m
  path: ./overlays/prod
  prune: true
  wait: true
  sourceRef:
    kind: GitRepository
    name: orders-api
  targetNamespace: orders
  serviceAccountName: orders-reconciler
  dependsOn:
    - name: platform-baseline
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: orders-api
      namespace: orders

wait: true plus healthChecks makes the Kustomization report Ready only once the Deployment is actually available, which is what makes dependsOn meaningful for anything downstream.

Argo CD:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: orders-api
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: payments
  source:
    repoURL: https://github.com/contoso/orders-api-config
    targetRevision: main
    path: overlays/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: orders
  revisionHistoryLimit: 10
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true
      - PruneLast=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

The resources-finalizer annotation is not optional in practice: without it, deleting the Application leaves every resource it created orphaned in the cluster.

Scaling Argo CD across clusters with an ApplicationSet:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: orders-api
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - clusters:
        selector:
          matchLabels:
            env: prod
  template:
    metadata:
      name: '{{.name}}-orders-api'
    spec:
      project: payments
      source:
        repoURL: https://github.com/contoso/orders-api-config
        targetRevision: main
        path: overlays/prod
      destination:
        server: '{{.server}}'
        namespace: orders
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

Set goTemplate: true explicitly. The legacy substitution syntax uses {{name}} without the leading dot, and mixing the two produces templates that render to literal braces rather than failing loudly.

The same chart, both ways — the Helm difference in YAML. Flux, performing a real release with test and remediation:

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: orders-api
  namespace: orders
spec:
  interval: 15m
  timeout: 5m
  releaseName: orders-api
  chart:
    spec:
      chart: orders-api
      version: '2.4.*'
      sourceRef:
        kind: HelmRepository
        name: contoso-charts
        namespace: flux-system
      interval: 5m
  install:
    remediation:
      retries: 3
  upgrade:
    remediation:
      retries: 3
  test:
    enable: true
  driftDetection:
    mode: enabled
  values:
    replicaCount: 3

Argo CD, rendering the same chart as manifests:

  source:
    repoURL: https://charts.contoso.example
    chart: orders-api
    targetRevision: 2.4.1
    helm:
      releaseName: orders-api
      skipTests: true
      valueFiles:
        - $values/overlays/prod/values.yaml

Same chart, same values, two different runtime contracts. Note that Argo CD requires a concrete targetRevision rather than Flux's semver range — version bumps become Git commits, which some teams consider the feature and others consider the friction.


The Decision Framework

flowchart TD
    A[Do product teams need to see and drive deploys themselves] -->|Yes| B[Argo CD]
    A -->|No| C[Is the estate hybrid or Arc-enabled]
    C -->|Yes| D[Flux via the Azure extension]
    C -->|No| E[Do you need Azure Policy to enforce GitOps fleet-wide]
    E -->|Yes| D
    E -->|No| F[Do your charts rely on Helm hook semantics]
    F -->|Yes| G[Flux]
    F -->|No| H[Either works - pick on team capacity]
    B --> I[Budget for operating the control plane]
    D --> J[Build notification alerts before go-live]

Choose Flux when a central platform team owns the clusters, the estate spans AKS and Arc, Azure Policy governance matters, or your Helm estate depends on real release semantics. Choose it especially if nobody has capacity to operate another control plane.

Choose Argo CD when multiple product teams need self-service deployment visibility, when on-call engineers without kubectl access need to diagnose and roll back, when SSO-scoped permissions per team are a requirement, or when a central multi-cluster view is worth operating a control plane for.

Running both is legitimate and more common than the tooling debate suggests: Flux for platform infrastructure through the Azure extension, Argo CD for application workloads with a UI for product teams. The cost is two reconciliation models to reason about, so give each a clearly bounded scope — one namespace should have exactly one owner — and never let both manage the same resource.


Pitfalls to Avoid

  • Leaving selfHeal off in Argo CD and assuming drift is corrected. Both prune and selfHeal default to false. Set them explicitly and state the intent in review.
  • Omitting serviceAccountName on a Flux Kustomization in a multi-tenant cluster. Without --default-service-account set, the reconciliation runs with the controller's own permissions and your tenant boundary is decorative.
  • Assuming helm ls will show Argo CD-deployed charts. It will not. Train the on-call runbook on Argo's history and rollback instead.
  • Expecting Argo CD sync waves to order across Applications. They order within one Application. Cross-app ordering needs app-of-apps, and it is less reliable than Flux's dependsOn.
  • Deleting an Argo CD Application without the resources finalizer. The Application disappears; the workloads stay, unmanaged, until someone finds them.
  • Letting the Flux extension auto-upgrade straight into production. Verify the behaviour in a non-production cluster and stage the rollout.
  • Putting the GitOps agent's own manifests in the same repository path it reconciles without careful bootstrapping. A bad commit can leave the agent unable to fix itself.
  • Running Flux with no notifications. Silent reconciliation is the design; silent failure is the consequence if you never wire notification-controller to anything.

Troubleshooting

Flux Kustomization is Ready but the workload never appeared

Check spec.targetNamespace and whether the impersonated service account can create the resource kind. Impersonation failures surface as apply errors in the Kustomization's status conditions rather than as a top-level failure. Run flux get kustomizations --all-namespaces and then kubectl describe kustomization <name> -n flux-system for the full condition text.

Flux stopped reconciling after an extension upgrade with a cross-namespace error

The multi-tenancy lockdown blocks references to sources or secrets in another namespace. Move the referenced object into the tenant namespace, or confirm the current opt-out configuration in Microsoft's GitOps documentation for your extension version.

Argo CD shows OutOfSync forever on a resource nobody is editing

Almost always a mutating admission controller or a defaulted field the API server adds after apply. Add a ignoreDifferences entry for the specific JSON path rather than disabling self-heal for the whole application.

Argo CD application syncs, then immediately shows OutOfSync again

A controller in the cluster is fighting Argo CD for the same field — a horizontal pod autoscaler managing spec.replicas is the classic case. Ignore that path explicitly, and remove replicas from the manifests in Git so there is nothing to fight over.

A Helm chart works with helm install but fails under Argo CD

Check for Helm hooks and the lookup function. Argo CD renders without cluster access, so lookup returns nothing, and Helm hooks are translated into Argo CD's hook model rather than executed by Helm. If the chart genuinely needs release semantics, deploy that chart with Flux's HelmRelease instead of forcing it.

az k8s-configuration flux create fails with an extension error

The Flux configuration commands need both CLI extensions present and the Microsoft.KubernetesConfiguration resource provider registered on the subscription. Run az extension add --name k8s-configuration, az extension add --name k8s-extension, and az provider register --namespace Microsoft.KubernetesConfiguration, then retry.


Key Takeaways

  • The reconciliation model is the same in both tools. The operator experience, the Helm contract, and the ownership of upgrades are what actually differ.
  • On AKS, Flux is the first-party option — microsoft.flux as a managed cluster extension, driven by az k8s-configuration flux, enforceable fleet-wide with Azure Policy, and identical for Arc-enabled clusters. Argo CD you install, secure, and upgrade yourself.
  • Argo CD's UI is a real operational capability, not a nicety: live diff, resource tree, sync history, and rollback without cluster credentials.
  • Helm behaves differently. Argo CD templates and applies; Flux performs a real Helm release with hooks, tests, and rollback. Charts with hook-heavy logic notice the difference.
  • Ordering differs. Flux's dependsOn orders across objects cluster-wide; Argo CD's sync waves order only within one Application.
  • On AKS, prefer External Secrets Operator with Key Vault under either tool. SOPS support is a Flux convenience, not a deciding factor.
  • Defaults matter more than features: Argo CD ships with prune and self-heal off, and Flux corrects drift silently unless you wire up notifications.

Next Steps

  1. Pilot on one non-production AKS cluster with a single real application, not a hello-world. Deploy it, then break it deliberately with a kubectl edit and watch what each tool does and how you find out.
  2. Test your three most complex Helm charts under Argo CD specifically, looking for hook-dependent behaviour and lookup usage. This is the fastest way to discover whether the templating model is a problem for your estate.
  3. Stand up the tenancy model before the second application. For Flux that means namespaces, service accounts, and the controller lockdown flags. For Argo CD it means AppProjects, Entra ID SSO, and the RBAC policy file.
  4. Wire notifications first, not last. Flux's notification-controller into Teams or Event Grid; Argo CD's notifications controller into the same channel. A GitOps agent nobody hears from is a deployment system with no feedback loop.
  5. If you choose the Flux extension, write an Azure Policy assignment that deploys your platform baseline configuration to every AKS cluster in scope, and confirm a freshly created cluster reconciles without manual steps.
  6. Decide the drift policy explicitly and record it. Which namespaces self-heal, which tolerate manual intervention during an incident, and how a break-glass change gets back into Git afterwards.

Related Articles


Last reviewed: 2026-08-09 | Tested against: Flux v2 APIs (source.toolkit.fluxcd.io/v1, kustomize.toolkit.fluxcd.io/v1, helm.toolkit.fluxcd.io/v2), Argo CD argoproj.io/v1alpha1, and the microsoft.flux AKS cluster extension. Both projects move quickly and the Azure extension trails upstream — verify API versions, extension defaults, and CLI flags against current documentation before production use.

Thorsteinn Halldorsson Senior Cloud Engineer

Senior Cloud Engineer with 25+ years of hands-on experience across the datacenter-to-cloud stack: fiber SAN and disk storage, IBM/Lenovo blade and Dell/HP/Lenovo servers, Hyper-V and VMware clusters, and SQL and Remote Desktop Services (RDS) clusters. Deep in the Microsoft platform — Active Directory, PKI/certificate services, SQL, Power BI, Dynamics 365 Business Central (NAV) and AX (Axapta), Microsoft 365, Entra, and Intune — with a focus on Azure operations, FinOps, and applying AI tools like GitHub Copilot and Claude in real workflows. Writes practical, no-nonsense guides for IT professionals who need to ship real solutions.

Leave a Reply

Your email address will not be published. Required fields are marked *