Webinar replay: Heroku to AWS in one command, with an agent doing the work.

GitOps at Scale: How to Enforce Consistent Sync Policies, RBAC, and Audit Trails Across Multiple Kubernetes Clusters

A practical comparison of Argo CD, Flux CD, Harness, and internal developer platforms for multi-cluster GitOps - and how to get consistent sync policies, per-environment RBAC, and full deployment audit trails without hand-configuring every cluster.

Romaric Philogene
CEO & Co-founder
OCT 2, 2026 · 10 MIN
GitOps at Scale: How to Enforce Consistent Sync Policies, RBAC, and Audit Trails Across Multiple Kubernetes Clusters

Git is not an audit trail. It is one third of one.

I talk to a lot of platform teams who run Argo CD or Flux across three, five, sometimes a dozen clusters, and the conversation always bends the same way. The reconciliation works. The drift gets caught. What keeps them up at night is the governance question underneath: who may deploy what, where, and with whose approval, and can you prove it six months later when a security reviewer asks. GitOps engines were built to answer the first question and say almost nothing about the second.

Qovery · Agentic Infrastructure Platform
A control plane for platform teams and their coding agents
Learn more

Key points:

  • Argo CD and Flux CD both reconcile cluster state well, but neither ships a central control plane for policy, RBAC, and approval audit trails across many clusters. Teams fill that gap with Argo CD ApplicationSets plus Kyverno or OPA Gatekeeper, a commercial layer like Harness, or an internal developer platform.
  • Pick Flux CD if a small platform team owns everything and wants a minimal, Git- and Terraform-driven toolkit. Pick Argo CD if you have many teams and need a UI, SSO-backed RBAC, and ApplicationSets to template apps across clusters.
  • For multi-cluster consistency, the durable pattern is one control-plane repo plus generators (Argo CD ApplicationSets or Flux Kustomize overlays), so per-cluster config is generated rather than hand-written.
  • A defensible audit trail needs three joined layers: Git and PR history for desired state, Kubernetes and cloud control-plane audit logs for what was actually applied, and a separate approval record tied to a real identity. Argo CD and Flux cover the first two.
  • An internal developer platform sits above the GitOps engine. It gives developers self-service deployments with per-environment RBAC and a central deployment history, while Argo CD or Flux keep reconciling platform components underneath.

What actually breaks when you run GitOps across three or more Kubernetes clusters?

The reconciliation scales fine. Governance is what breaks first, and it breaks quietly, because sync policies, RBAC, and approval records get configured per cluster and drift apart within weeks.

Four failure modes show up again and again:

  • Divergent sync policies. Cluster one has automated sync with prune: true and selfHeal: true. Cluster three was set up by a different engineer six months later with manual sync. Nobody decided this; it accreted. Now a change that is auto-pruned in staging sits un-applied in production, or worse, auto-prune is accidentally live on prod and quietly deletes a resource someone created by hand during an incident.
  • RBAC defined per cluster or per Argo CD instance. Each Argo CD gets its own argocd-rbac-cm ConfigMap or its own set of AppProjects. Grant a new team read access everywhere and you are editing N files, hoping you did not fat-finger one.
  • No single place to see who approved a promotion. Someone synced to prod on Tuesday. Which human? Under which identity? You can reconstruct it from three systems if you have an afternoon.
  • Secrets handled differently per cluster. Sealed Secrets here, External Secrets Operator there, a SOPS file someone committed once. Each cluster is a slightly different snowflake.

There is also an architecture fork that sets the ceiling on all of this. You either run hub-and-spoke (one Argo CD managing N external clusters) or one instance per cluster. The real trade is blast radius against consistency: a single hub gives you one RBAC model and one UI but one controller whose outage or compromise touches everything, while per-cluster instances contain the blast radius and multiply the config you have to keep in sync. Flux is almost always run per cluster; Argo CD does both.

The thing nobody budgets for is upgrades. Every cluster carries controller versions, CRDs, and a Kubernetes version that all need bumping. "Just add more YAML" fails here because the config is hand-written instead of generated, so every new cluster is a manual onboarding and every upgrade is a manual sweep. The practitioner complaints you see on the CNCF Slack and r/kubernetes are rarely "Argo CD cannot reconcile." They are "we have eleven Argo CD instances and no idea which policies match."

FluxCD vs ArgoCD for multi-cluster GitOps: which should you pick?

I will not fence-sit on this one, because a non-answer is dishonest. Choose Argo CD if many teams need a UI, SSO-backed RBAC, and app templating across clusters. Choose Flux if one platform team owns everything and wants a minimal, composable, Git- and Terraform-driven setup. Both are CNCF Graduated, the foundation's top maturity tier: Flux graduated on 30 November 2022 and Argo on 6 December 2022 (CNCF on Flux, CNCF on Argo). Neither is a risky bet.

Architecturally they differ in shape. Argo CD is an application controller plus an API server and a first-class web UI, with an opinionated application model. Flux is a set of composable GitOps Toolkit controllers (source, kustomize, helm, notification, image automation) that you assemble, with no bundled UI. That difference drives everything downstream.

As an adoption proxy, on 2 October 2026 the argoproj/argo-cd repo sits at about 24,300 GitHub stars and fluxcd/flux2 at about 8,400 (Argo CD repo, Flux repo). Stars are a popularity signal, not a quality verdict, and part of Argo's lead is that the UI draws individual users while Flux tends to be installed once by a platform team. Both are heavily used in production.

DimensionArgo CDFlux CD
Multi-cluster modelHub registers external clusters; ApplicationSets cluster generator fans apps outInstalled per cluster, fanned out via Kustomize overlays or the Terraform flux provider
RBAC and multi-tenancyAppProjects + argocd-rbac-cm policy + OIDC group mappingKubernetes RBAC + serviceAccountName impersonation per tenant namespace
UI and visibilityFirst-class web UI with live diffNo bundled UI; diffs via CLI and events
Policy enforcementNone built in; pair with Kyverno or OPA GatekeeperNone built in; pair with Kyverno or OPA Gatekeeper
Secrets handlingExternal Secrets Operator, Sealed Secrets, or SOPS (plugin)SOPS natively in the kustomize controller, or External Secrets Operator
Progressive deliveryArgo RolloutsFlagger
Approval audit recordAssembled from Git + API + events; not a single logAssembled from Git + notification controller events
Operational overheadHeavier (API server, UI, Redis)Lighter (controllers only)
CNCF maturityGraduated (Dec 2022)Graduated (Nov 2022)

On multi-cluster specifically: Argo CD's ApplicationSets cluster generator is the feature that makes a hub model bearable. It templates one Application per registered cluster from a single definition, so a new cluster is a registration plus a label, not a new folder. Flux leans on flux bootstrap per cluster, Kustomize overlays for per-environment variance, and the Terraform flux provider when you want to drive that from your existing IaC. Both work; they feel completely different day to day.

What are the best CI/CD pipelines and tools for GitOps workflows?

The best GitOps setups split three responsibilities instead of cramming them into one tool: CI builds and signs artifacts, a GitOps engine reconciles cluster state, and a control layer owns approvals, RBAC, and audit. Blur those and you get the anti-pattern I see most: a CI job running kubectl apply straight to prod with a static kubeconfig in a secret. That throws away attribution, drift detection, and rollback in one line.

CI should never touch the cluster directly. It should bump an image tag or open a PR against the config repo, and let Argo CD Image Updater or Flux image automation carry it the rest of the way. That keeps deployment pull-based, which matters for any cluster you do not want to expose an inbound API to: the agent inside the cluster pulls desired state out, so you never open a hole for a push.

CI toolGitOps enginePromotion mechanismApproval + audit layerBest-fit team size
GitHub ActionsArgo CDPR to config repo + Argo CD syncGitHub Environments required reviewersSmall to large
GitLab CIFluxImage policy + Kustomize overlay per envGitLab protected environmentsSmall to mid
TektonArgo CDApplicationSet + sync windowsAppProject + OIDC groupsMid to large, cloud-native shops
JenkinsArgo CDPR bump, manual syncJenkins approval + Argo CD RBACLarge, legacy estates
Any CIArgo CD or Flux + platformEnvironment promotion gateInternal developer platformMany product teams, small platform team

Where promotion between environments actually happens varies more than people expect: PR-based promotion between branches or folders, Argo CD sync windows that freeze prod outside a change window, Kargo for staged promotion pipelines on top of Argo CD, or Flux image policies gating which tags reach which environment. An internal developer platform fits in that last row, owning developer self-service, preview environments per pull request, environment auto-stop for non-production, and managed cluster upgrades, so the platform team is not the bottleneck for every promotion.

How do you centralize sync policies and RBAC across multiple Argo CD clusters without configuring each one?

This is the question I hear most, usually phrased as "we have three clusters and we are tired of editing three files." Three approaches work, and the strongest teams use all three together.

Pattern 1, the control-plane repo. One repo generates everything. Use an ApplicationSet with the cluster generator so adding a cluster is a one-line label change, not a new directory, and the git generator to template apps from a folder structure. Make AppProject objects your RBAC and guardrail boundary: an AppProject restricts which repos, destinations (clusters and namespaces), and resource kinds its applications may touch, and it is where syncWindows live. One AppProject template applied through the ApplicationSet means every cluster inherits the same boundary.

Pattern 2, policy as code shipped by GitOps itself. Argo CD has no policy engine, so you distribute one. Put Kyverno or OPA Gatekeeper ClusterPolicy objects in the control-plane repo and let Argo CD install them on every cluster, validating resource limits, allowed image registries, required namespace labels. The policy bundle becomes just another app that reconciles everywhere, so its version is consistent by construction. Add Argo CD resource exclusions so the controller ignores things it should not manage, and sync windows for change freezes.

Pattern 3, a platform layer. Put one RBAC and environment model on top that is applied identically on every cluster and cloud, so there is no per-cluster RBAC file to drift in the first place. More on where that fits below.

The Flux equivalent of patterns 1 and 2 is one bootstrap repo, Kustomize overlays per cluster, the Terraform flux provider to drive bootstrap from IaC, and tenant namespaces isolated with serviceAccountName impersonation so a tenant's Kustomization can only act as its own service account. Pick one secrets approach (External Secrets Operator, Sealed Secrets, or SOPS) and enforce it everywhere; mixing them is how clusters become snowflakes.

Here is the checklist I give teams for what "consistent" actually has to mean. If any item differs between clusters and you did not decide it on purpose, that is drift:

[ ] Sync options identical (CreateNamespace, ApplyOutOfSyncOnly, etc.)
[ ] Prune and selfHeal set the same way per environment class
[ ] Retry backoff and limits identical
[ ] RBAC groups mapped from the same OIDC provider, same role names
[ ] Policy bundle (Kyverno/Gatekeeper) pinned to one version everywhere
[ ] Notification routes point to the same channels with the same triggers
[ ] One secrets mechanism, no exceptions

Which GitOps tools have built-in policy enforcement?

The honest answer surprises people: Argo CD and Flux have no built-in policy engine. Enforcement comes from a layer you add, and each layer blocks different things at a different moment. Be clear about which is which, because a reviewer will be.

LayerExample toolsWhere it runsWhat it can blockAudit outputEffort across N clusters
AdmissionKyverno, OPA Gatekeeper, ValidatingAdmissionPolicyIn-cluster, at the API serverAny non-compliant manifest, regardless of who sent itPolicy reports, API audit eventsDistribute via GitOps; one bundle
PipelineHarness, Octopus Deploy, Spacelift, env0In the CI/CD pipelineDeploys outside approval, freeze windows, missing reviewersPipeline run history, approval recordsCentral if the tool is central
Supply chainSigstore/cosign verified by KyvernoAt admissionUnsigned images, missing SBOM attestationsVerification eventsPart of the policy bundle
InfrastructureSpacelift, env0, Crossplane compositionsAt plan/apply timeNon-compliant cluster or cloud resourcesPlan logs, policy checksPer IaC stack
PlatformInternal developer platformAbove the engineWhat a developer can deploy or reach at allDeployment history, RBAC logsOne model, all clusters

At admission, the trend to know is Kubernetes ValidatingAdmissionPolicy, which uses CEL expressions evaluated inside the API server with no webhook. It went GA in Kubernetes 1.30 (Kubernetes blog), and it is pulling simple validation in-tree while Kyverno and OPA Gatekeeper stay ahead on mutation, generation, and richer policy. Kyverno itself reached CNCF Graduated status in March 2026 (CNCF), joining OPA at the top tier.

The key limit to say out loud: admission policy blocks a bad manifest but says nothing about whether a human approved the change. Pipeline and platform layers own approval. You need both.

Ship faster on infrastructure you control.
Qovery gives your team self-service deployments with per-environment RBAC and a central deployment history - on your own AWS, GCP, Azure, or Scaleway account, or your existing Kubernetes cluster. Start deploying in under 10 minutes.

Argo CD vs Harness for Kubernetes: which gives better policy controls and pipeline integration?

Start with the fact that reframes the whole comparison: Harness GitOps is built on Argo CD. Harness supports Argo CD as its only GitOps reconciler (Harness docs). So this is not engine against engine; it is "assemble the governance yourself" against "buy it pre-assembled."

Argo CD gives you the strongest open-source reconciliation engine, free, fully inspectable, with a large ecosystem (ApplicationSets, Notifications, Rollouts) and RBAC via AppProjects and OIDC groups. The cost is that policy, approvals, and audit are yours to build. The audit trail is assembled from Git history, Kubernetes events, and the Argo CD API, not handed to you as one screen.

Harness ships the governance out of the box: Policy as Code with OPA/Rego evaluated on save or sync, approval stages, deployment freeze windows, and an audit trail UI (Harness Policy as Code, deployment freeze). One honest caveat worth knowing: Harness evaluates its GitOps policies on syncs triggered through the Harness UI or API, not on automatic syncs that Argo CD performs on its own, so you design around that. The cost is license spend and vendor coupling.

CapabilityArgo CD + OSS add-onsHarnessOctopus DeployQovery
Policy controlsKyverno/Gatekeeper (you add)Built-in OPA Policy as CodeBuilt-in rules + runbooksPer-environment RBAC + lifecycle rules
Approval gatesGitHub Environments / Argo sync windowsBuilt-in approval stagesBuilt-in manual interventionApproval-gated promotion
Audit trailAssembled from Git + events + APIBuilt-in audit UIBuilt-in audit logCentral deployment history
Multi-cluster RBACAppProjects + OIDC, per instanceCentralCentralOne model across clusters and clouds
CI integrationAny CI via PR/image bumpNative pipelines + any CIAny CIGit-push + any CI
Hosting modelSelf-hosted OSSSaaS or self-managedSaaS or self-hostedBYOC on your cloud or cluster
Cost modelFree (you run it)Commercial licenseCommercial licenseCommercial, runs in your account
Who operates itYour platform teamHarness (SaaS)You or OctopusQovery control plane, your infra

My rule of thumb: Argo CD plus Kyverno plus GitHub Environments is genuinely enough when you have a platform team that enjoys running this stack and wants everything inspectable and free. A commercial layer like Harness pays for itself when you are out of platform-engineering hours and would rather buy approval stages, freezes, and an audit UI than build and maintain them. For fairness, Octopus Deploy, Rancher, Spectro Cloud, and Red Hat OpenShift GitOps all sit in adjacent territory, each stronger on a different axis (release management, fleet management, edge, OpenShift-native).

How do you get a full audit trail of who approved what and when for every Kubernetes deployment?

Back to the opening claim. Git is one third of an audit trail. A defensible one joins three layers, all shipped to the same store with a shared correlation ID:

Layer 1, Git: desired state and the human intent behind it. Signed commits, protected branches, required reviewers, and the PR approval metadata (who approved, when, which reviewers). This is where "what did we intend to run, and who signed off on the change" lives. It is strong and cheap. It is also only one third, because Git records what you meant to apply, not what actually landed on the cluster or who pressed sync.

Layer 2, cluster and cloud: what was actually applied. Kubernetes API server audit logs (set an audit policy, ship them off-cluster), plus the cloud control plane: EKS control-plane logs to CloudWatch, GKE Cloud Audit Logs, Azure activity logs. Argo CD emits events and Flux has its notification controller. This is the record of the real mutation.

Layer 3, approval: a gate tied to a real identity. GitHub Environments required reviewers, Argo CD manual sync performed under an SSO identity with sync windows, Harness approval stages, or an internal developer platform with per-environment deploy permissions. This is the "who approved this specific promotion" record that neither Git nor the API log gives you cleanly on its own.

The glue is correlation. Carry the commit SHA and PR number into resource annotations and labels so a running pod traces back through the deploy to the approver. Without that thread, you have three logs and no story. Ship all three to one SIEM (Splunk, Datadog, Loki) with retention that matches your obligations; SOC 2 and ISO 27001 evidence reviews want to see that the records exist, are tamper-resistant, and are retained.

The gaps auditors flag most, in the order I see them caught:

  • Shared service accounts. Everything was done by ci-bot, so the log cannot name a human.
  • Cluster-admin kubeconfigs on laptops. A direct kubectl path that bypasses every gate.
  • Auto-sync with no human record. The change reconciled itself; no approval row exists anywhere.
  • Emergency kubectl edit during an incident that GitOps silently reverts on the next sync, leaving a change that happened and then un-happened with no trace of either.

Where does an internal developer platform like Qovery fit alongside Argo CD or Flux?

Here is the boundary I want to be precise about, because getting it wrong is how these articles lose credibility: Qovery does not replace Argo CD or Flux. It sits above the GitOps engine as the control layer, giving developers self-service environments while the platform team keeps one RBAC model, one approval flow, and one deployment history across every cluster and cloud.

What Qovery owns is the application-delivery layer: git-push deployments, preview environments per pull request, environment auto-stop for non-production so idle environments stop burning money, managed cluster upgrades, per-environment RBAC, and databases backed by managed cloud services. Where it runs is the part that matters for the governance story: your own AWS, GCP, Azure, or Scaleway account under BYOC, or your existing self-managed Kubernetes cluster on any provider or distribution. Because it is BYOC, the cloud bill and any Savings Plans or committed-use discounts stay in your name, not a vendor's.

The multi-cluster win is direct. Instead of N Argo CD RBAC files to keep in sync, you have one environment and permission model applied identically everywhere. The failure modes from the first section (divergent sync policy, per-cluster RBAC, no single approval record) are exactly what a single control layer collapses into one place.

The honest boundary: keep Argo CD or Flux for platform add-ons, operators, and arbitrary CRDs. That is their sweet spot and nothing above should pretend otherwise. Use a platform layer for application delivery, where developer self-service and consistent permissions are the whole game.

When I interview CTOs, almost nobody says they want to maintain GitOps plumbing; they want their product teams shipping safely without the platform team gating every deploy. So the decision guide is about team shape, not tool quality. A small platform team serving many product teams is the clearest case for a platform layer. A dedicated platform-engineering org that genuinely wants to run Argo CD or Flux plus a policy engine themselves can absolutely do that, and do it well.

Is Flux CD or Argo CD better for multi-cluster GitOps?

Neither is universally better. Choose Argo CD when many teams need a UI, SSO-backed RBAC, and ApplicationSets to template apps across clusters. Choose Flux when one platform team owns everything and wants a minimal, composable, Git- and Terraform-driven setup. Both are CNCF Graduated and production-proven.

Can Argo CD enforce the same sync policy and RBAC across multiple clusters?

Not on its own, but the pattern is well established: generate everything from one control-plane repo using ApplicationSets (the cluster generator for fan-out, the git generator for templating) and make AppProjects your shared RBAC and sync-window boundary. Argo CD has no policy engine, so distribute Kyverno or OPA Gatekeeper policies through the same repo to enforce guardrails everywhere.

Which GitOps tools have built-in policy enforcement?

Argo CD and Flux have none built in. Enforcement comes from admission engines (Kyverno, OPA Gatekeeper, or the in-tree ValidatingAdmissionPolicy, GA in Kubernetes 1.30), from commercial pipeline layers (Harness, Octopus Deploy, Spacelift, env0), or from a platform layer that constrains what developers can deploy at all.

Does Argo CD or Flux CD provide an audit trail of who approved a deployment?

Partly. They cover desired state (Git history) and, through events and cloud audit logs, what was applied. Neither gives you a clean approval record tied to a real identity on its own. You add that with GitHub Environments, SSO-gated manual sync, Harness approval stages, or a platform with per-environment deploy permissions, and correlate the three layers with the commit SHA.

Is Harness better than Argo CD for multi-environment Kubernetes deployments?

Harness GitOps is built on Argo CD, so the engine is the same. Harness wins on out-of-the-box Policy as Code, approval stages, deployment freezes, and an audit UI. Argo CD wins on cost, inspectability, and ecosystem. Buy Harness when you are out of platform-engineering hours; stay on Argo CD plus Kyverno and GitHub Environments when you want everything free and inspectable.

Do I still need Argo CD or Flux if I use an internal developer platform like Qovery?

Keep Argo CD or Flux for platform add-ons, operators, and arbitrary CRDs; that is their sweet spot. Use Qovery as the control layer above for application delivery, giving developers self-service environments with per-environment RBAC and a central deployment history across AWS, GCP, Azure, Scaleway, or your own existing Kubernetes cluster. The two complement each other rather than compete.

The pattern I keep coming back to is this: reconcile with Argo CD or Flux, enforce guardrails at admission with Kyverno, and own approval and history in one layer above instead of N configs you hand-edit. If you want that top layer without assembling it yourself, try Qovery free and give your team self-service deployments with per-environment RBAC and central deployment history on infrastructure you already own.

Romaric Philogene
About the author
Romaric Philogene

Romaric founded Qovery to make Kubernetes accessible to every engineering team. He writes about platform strategy, developer experience, and the future of cloud infrastructure.

Next step

Ship faster on infrastructure you control.

Qovery gives your team self-service deployments with per-environment RBAC and a central deployment history - on your own AWS, GCP, Azure, or Scaleway account, or your existing Kubernetes cluster. Start deploying in under 10 minutes.