Argo CD and Flux are the two CNCF-graduated GitOps tools for Kubernetes. Argo CD suits teams that want a built-in UI, ApplicationSets for managing many clusters, and app-centric workflows, and Flux suits teams that want a lightweight, composable toolkit driven by Kustomizations and HelmReleases. For environments, use a folder or overlay per environment on one main branch instead of long-lived branches per environment, and add progressive delivery to either tool for higher-risk rollouts. Control Plane’s Kubernetes Operator lets Argo CD manage Control Plane workloads, policies, and Global Virtual Clouds (GVCs) from Git. A GVC is Control Plane’s term for the set of locations a workload runs across, and Control Plane handles Day 2 operations and runs each workload active-active across the regions and clouds in its GVC.
Disclosure: Control Plane publishes this guide.
TL;DR
- Argo CD runs a server with a web UI, SSO, project-level RBAC, and ApplicationSet generators for templating applications across a fleet.
- Flux runs Kubernetes-native controllers, with full Helm release lifecycle management, optional image automation controllers, and native SOPS decryption.
- Environment layout: one main branch with a folder per environment, using Kustomize overlays or Helm values files.
- Multi-cluster: Argo CD is most often run as a hub that manages spoke clusters, and Flux is most often run as independent controllers in each cluster; both tools support the other model.
- Rolling updates and rollback: start with Kubernetes rolling updates and readiness probes, add canary or blue-green analysis with Argo Rollouts or Flagger, and roll back by reverting the Git commit.
- Long-lived infrastructure: manage networks, databases, and clusters with Terraform, OpenTofu, Pulumi, or Crossplane, and deliver applications with GitOps.
- How Control Plane fits: the Control Plane Kubernetes Operator exposes GVCs, workloads, identities, secrets, policies, domains, and other Control Plane resources as custom resources that you keep in Git for Argo CD to reconcile.
Which GitOps tool is best for separating per-environment branches, applications, or clusters?
It depends on your operating model. Argo CD is an application with a central server and UI, and Flux is a set of composable controllers. Both reconcile desired state from Git, and they differ in architecture, default multi-cluster model, and developer experience.
| Dimension | Argo CD | Flux |
|---|---|---|
| Architecture | API server, repo server, and application controller (docs) | Independent source, kustomize, helm, and notification controllers, plus optional image automation controllers (docs) |
| UI | Built-in web UI with sync status, resource tree, diff view, and logs | No UI in the core controllers; operated through the CLI and Kubernetes resources, with a web UI in the Flux Operator and community dashboards such as Headlamp’s Flux plugin |
| Multi-cluster model | Usually hub and spoke, where one instance manages many registered clusters; also runs as one instance per cluster, and the argocd-agent project adds pull-based agents for clusters without inbound access | Usually one Flux install per cluster pulling from Git; a hub Flux can also deploy to remote clusters through kubeConfig references |
| App and environment generation | ApplicationSet generators create Applications from lists, Git directories, cluster selectors, or matrices | Kustomization and HelmRelease resources with post-build variable substitution per environment, plus ResourceSets in the Flux Operator for templating apps from inputs such as pull requests |
| Helm support | Renders charts with helm template and manages the output as manifests, without Helm release history; multi-source Applications combine a chart from a registry with values files from a Git repository | Full Helm release lifecycle through the helm-controller, including drift detection on releases |
| OCI artifacts as a source | Helm charts from OCI registries, and OCI artifacts as a native Application source since v3.1 (beta) | OCIRepository source for manifests and charts, with flux push artifact to publish from CI |
| Image update automation | Argo CD Image Updater, a separate project | Optional image reflector and image automation controllers, added at bootstrap with –components-extra, that scan registries and commit updates to Git |
| Multi-tenancy and RBAC | Projects, SSO integration, fine-grained RBAC, and sync windows | Kubernetes RBAC with service account impersonation, so each tenant’s Kustomizations run with that tenant’s permissions |
| Notifications | Argo CD Notifications with triggers and templates for Slack, GitHub, webhooks, and more | Notification controller with alerts and event forwarding to Slack, Teams, webhooks, and Git commit statuses |
| Progressive delivery companion | Argo Rollouts for canary, blue-green, and analysis-driven promotion | Flagger for canary, blue-green, and A/B testing with metric analysis |
| CNCF status | Graduated | Graduated |
Choose Argo CD if your team values a visual dashboard for sync status and health, needs central SSO and RBAC across teams sharing clusters, wants ApplicationSet generators to template many applications across a fleet, or wants operators to troubleshoot sync failures in a UI.
Choose Flux if you want every cluster to pull its own state with no central server, you prefer Kubernetes-native resources over a separate application, you rely on Helm releases and want their full lifecycle managed, or your platform team prefers composing small controllers.
How should I structure GitOps repositories for environments, apps, and clusters?
Repository layout decides how clearly you can see what runs where, how promotion works, and who can change what. Three patterns dominate.
| Pattern | How promotion works | Pros | Cons | Best fit |
|---|---|---|---|---|
| Branch per environment | Merge or cherry-pick from a dev branch to a staging branch to a prod branch | Familiar Git workflow | Merge conflicts accumulate, drift between branches is hard to see, and config diverges over time | Small teams with one app and few environments |
| Folder per environment (recommended) | A pull request changes the image digest in the next environment’s overlay folder on the main branch | One branch as the source of truth, with differences between environments visible in the directory tree | Needs consistent folder naming and overlay structure | Most teams, as the default |
| App repos plus a shared config repo | Each team owns an app repo, and CI opens a pull request against a central config repo to promote | Clear ownership boundaries, scales to many teams, and CODEOWNERS enforces review | More repositories, and cross-cutting changes span several pull requests | Large organizations with separate platform and application teams |
For multi-cluster setups, add a clusters directory where each cluster’s folder references the apps and platform components it runs, so the rendered manifests for each cluster are unambiguous. In Argo CD, a root Application (app of apps) or an ApplicationSet with a Git directory generator creates one Application per folder, for example apps/*/overlays/prod. In Flux, flux bootstrap writes a flux-system folder under the –path you pass, conventionally clusters/CLUSTER_NAME, and that folder points Kustomizations at infrastructure and apps paths, with infrastructure ordered first through dependsOn.
Promotion in every folder-based pattern follows the same flow. CI builds and publishes an immutable image, a pull request updates the image digest in the target environment’s overlay, a reviewer approves it, and the GitOps controller reconciles the approved commit. To automate that pull request, use Argo CD Image Updater with Git write-back, Flux image automation pushing to a branch, or Kargo, which promotes a release through stages with verification between them. This flow matches the OpenGitOps principles: declarative, versioned and immutable, pulled automatically, and continuously reconciled.
What is the best platform for deploying manifests from Git to clusters with minimal kinks?
Argo CD and Flux both deploy manifests from Git reliably once you fix the same six problems most teams hit.
| Kink | Why it happens | Fix in Argo CD | Fix in Flux |
|---|---|---|---|
| Sync loops from mutating webhooks and defaulted fields | The API server or admission controllers change fields the Git manifest also sets, so the resource never looks in sync | Enable server-side diff (the ServerSideDiff=true compare option, used automatically with the ServerSideApply sync option), or set ignoreDifferences on the affected fields together with the RespectIgnoreDifferences sync option | Flux applies with server-side apply and leaves fields it does not set to their other managers; for fields it does set, use the kustomize.toolkit.fluxcd.io/ssa annotation with IfNotPresent or Ignore, or driftDetection ignore rules on a HelmRelease |
| Secrets in Git | Plaintext secrets in a repository are exposed to anyone with read access | Use External Secrets Operator or Sealed Secrets; Argo CD does not decrypt secrets itself, and its docs advise against secret injection plugins | Native SOPS decryption in the kustomize controller, plus External Secrets Operator or Sealed Secrets |
| CRD ordering | Custom resources applied before their CRD exists fail | Sync waves order CRDs before dependent resources, and the SkipDryRunOnMissingResource sync option avoids dry-run failures when the CRD is not installed yet | dependsOn between Kustomizations declares the order |
| Drift from manual kubectl changes | Direct kubectl edits create state that differs from Git | Enable automated sync with self-heal; Argo CD reports drift but does not correct it by default | Kustomizations revert drift on every interval by default; HelmReleases need driftDetection enabled; set prune to remove resources no longer in Git |
| Large monorepos | Every commit changes the repository revision, so the controller re-renders or re-fetches apps whose files did not change | Add the manifest-generate-paths annotation so a commit only refreshes apps whose paths changed, configure Git webhooks, scale and shard the repo server and application controller, and split very large repositories | Use spec.ignore or sparse checkout on the GitRepository so artifacts contain only needed paths, and use separate GitRepository sources for unrelated areas |
| Mutable image tags | Deploying latest or other mutable tags makes cluster state irreproducible from Git | Deploy by image digest, and use Argo CD Image Updater to write digests | Deploy by image digest, and use image automation to commit digest updates |
For drift, a safe progression is detection only, then automated sync in low-risk namespaces, then self-heal once ownership is clear, then pruning after you have tested deletion behavior. Treat manual kubectl changes as emergency overrides, and document a break-glass process for when someone must bypass Git.
What’s the best infrastructure management platform for long lived infrastructure, GitOps workflows, and rolling updates?
Manage long-lived infrastructure such as VPCs, managed databases, DNS zones, and Kubernetes clusters with infrastructure as code tools such as Terraform, OpenTofu, Pulumi, or Crossplane. These resources change rarely, need careful state management, and depend on cloud provider APIs. Infrastructure as code usually provisions the cluster, and GitOps delivers what runs on it. The line can move. Crossplane managed resources and compositions live in Git and are reconciled by Argo CD or Flux, and tofu-controller runs Terraform or OpenTofu plans as Flux custom resources, though it is community maintained, so check its release cadence before relying on it. For platforms that manage Kubernetes infrastructure and workloads together, see the best Kubernetes management platforms.
How do rolling updates and progressive delivery work with GitOps?
Kubernetes Deployments roll out updates with maxSurge and maxUnavailable settings, gated by readiness probes, which is enough for most workloads. For higher-risk changes, progressive delivery adds metric-driven analysis. Argo Rollouts provides canary and blue-green strategies with analysis steps that query Prometheus, Datadog, or other providers, and Flagger gives Flux users the same, shifting traffic to a canary and rolling back when error rates or latency cross thresholds.
Adopt these practices with either tool:
- Validate manifests and render Helm or Kustomize output in CI before merging.
- Promote immutable image digests through development, staging, and production in separate pull requests.
- Require approvals for production changes.
- Order syncs so CRDs and namespaces deploy before dependent resources.
- Watch error rates, latency, and saturation during rollouts, with automatic abort thresholds.
What makes a GitOps rollback safe?
A rollback is a Git revert that the controller reconciles. Avoid kubectl rollout undo on its own, because a controller that corrects drift (Argo CD with self-heal, or a Flux Kustomization) reapplies what is in Git. For an emergency rollback faster than a Git revert, pause reconciliation first (disable auto-sync in Argo CD, which argocd app rollback requires, or run flux suspend in Flux), roll back, then revert Git and resume. With Argo Rollouts or Flagger, an aborted canary returns traffic to the stable version, but you still revert Git so the desired state matches. A Git revert does not undo database migrations, so use expand and contract migrations that the previous release can still run against.
How Control Plane fits
Control Plane is the unbreakable platform for running production workloads. It runs serverless, standard, stateful, and cron workloads natively on AWS, GCP, and Azure, VM workloads on locations joined through Control Plane’s Managed Kubernetes (MK8s), which adds your own servers and cloud accounts as locations, and workloads on Kubernetes clusters you already run or your own machines, including on premises, active-active across regions and clouds.
The Control Plane Kubernetes Operator manages Control Plane resources from Kubernetes custom resources, so you can store them in Git and deploy them automatically with Argo CD. It covers resources including GVCs, workloads, identities, secrets, policies, domains, volume sets, cloud accounts, agents, and Managed Kubernetes clusters. Deleting one of these custom resources while the operator is running also deletes the matching Control Plane resource, so an Argo CD prune removes it from Control Plane; the cpln.io/resource-policy: keep annotation prevents that. Control Plane recommends exporting manifests from the console instead of writing them by hand.
A workload deployed to a GVC runs active-active in every location of that GVC from one definition, and a global endpoint routes each request to the nearest healthy location. The 99.999% availability SLA applies to workloads running at least two replicas in at least two locations.
Without Kubernetes, a CI pipeline can run cpln apply with cpln, Control Plane’s CLI, on YAML files in Git. A single cpln apply run applies resources in dependency order, so a workload and the GVC it references can live in the same file or directory. Control Plane publishes CI/CD examples for GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI, and Google Cloud Build.
Control Plane ships its own Terraform and Pulumi providers, so it complements the infrastructure as code you already run. Control Plane handles Day 2 operations for you, including autoscaling, built-in observability, and security, and a tamper-proof audit trail records who did what across the org. Customers typically cut compute costs 30 to 50 percent compared with running directly on AWS, GCP, or Azure. Control Plane is PCI DSS Level 1, SOC 2 Type II, HIPAA, and GDPR compliant.
For teams practicing platform engineering without a dedicated platform team, Control Plane provides the infrastructure layer so they can focus on GitOps workflows and application delivery.
FAQ
Short answers on GitOps tooling, repository structure, and safe rollouts.
Is Argo CD or Flux better for multi-cluster?
Argo CD is better for central management, where one hub manages many clusters with shared RBAC and ApplicationSet templating. Flux is better for decentralized setups, where each cluster runs its own controllers. For edge clusters and clusters with restricted inbound access, either Flux or Argo CD with argocd-agent works.
Should I use a branch per environment in GitOps?
Usually no. A folder per environment on one main branch is the better default, because environment branches cause merge conflicts, hide drift, and make it hard to compare staging and production at a given commit. Branches that tooling writes with fully rendered manifests, such as Argo CD’s Source Hydrator, are a different pattern and do not have this problem.
How do I roll back with GitOps?
Revert the change in Git. The GitOps controller then syncs the cluster to the reverted state, so the rollback is reviewed and recorded like any other change.
Can I use GitOps without Kubernetes?
Yes. The pattern of declarative configuration in Git with automated application works outside Kubernetes. On Control Plane, a CI pipeline runs cpln apply, a command in Control Plane’s CLI, on YAML files in Git, and one run applies every resource in dependency order with no Kubernetes cluster to operate.
How do I handle secrets in a GitOps repository?
Keep plaintext secrets out of Git. Sync them from a cloud secrets manager with External Secrets Operator, or encrypt them before committing with Sealed Secrets or SOPS. Flux decrypts SOPS natively, and Argo CD relies on operators such as External Secrets Operator.
What causes sync loops in Argo CD and Flux?
Fields that mutating webhooks or API server defaulting change on resources the Git manifest also defines. In Argo CD, use server-side diff or ignoreDifferences with RespectIgnoreDifferences; in Flux, server-side apply avoids most of them, and the ssa annotations or HelmRelease drift ignore rules handle the rest.
How do I promote changes between environments safely?
Build an immutable image in CI, open a pull request that updates its digest in the next environment’s overlay, get it reviewed, and let the controller reconcile the merged commit. Kargo, Argo CD Image Updater, or Flux image automation can open those pull requests for you.
Should I use one Git repository or several for GitOps?
One config repository suits small and medium teams. Larger organizations often give each team an app repository and keep environment overlays in a shared config repository, with CODEOWNERS setting review boundaries.
When should I add progressive delivery to my GitOps workflow?
Once a service carries enough production traffic that a bad release is costly, and you have metrics in a provider such as Prometheus or Datadog to drive automated analysis. Until then, rolling updates with readiness probes are enough.
How does Control Plane integrate with Argo CD?
Through the Control Plane Kubernetes Operator, which exposes Control Plane resources as Kubernetes custom resources. You keep them in Git next to your other manifests, and Argo CD reconciles GVCs, workloads, identities, policies, and domains like any other resource.
To combine GitOps with active-active workload delivery across regions and clouds, talk to the Control Plane team or sign up free.
Originally published on the Control Plane blog.
