Back to Learn
DevOps Tools

GitOps Explained: ArgoCD, Flux and Progressive Delivery

How the reconciliation loop actually works, how Argo CD and Flux differ once you live with them, what canary and blue-green add on top, and the secrets and drift problems tutorials skip.

GitOps Explained: Argo CD, Flux & Progressive Delivery
Firoz Ahmed, AWS Certified Solutions Architect & DevOps Lead
Sep 13, 2026
16 min read
Updated

A question I get in almost every batch, usually around week six: "Sir, if Jenkins already deploys to Kubernetes, why do we need ArgoCD?"

It's a good question and the textbook answer is bad. The textbook answer is "Git becomes your single source of truth," which means nothing to someone who has never watched a cluster drift away from its repo over six months until nobody trusts either one.

The useful answer takes a story. So: 11 PM, production is misbehaving, somebody patches a live resource from their laptop, alerts stop, everyone goes to bed. Next morning nobody can reconstruct what changed. Three weeks later a redeploy wipes the fix and the same bug returns.

That gap, between what the repo says and what's actually running, is what GitOps exists to close.

Below: how the mechanism works, how Argo CD and Flux differ once you're living with them, what progressive delivery adds, and the messy parts nobody puts in tutorials. Basic Kubernetes assumed. If Deployments and Services are new to you, read Kubernetes explained simply first.

What Is GitOps, Actually?

GitOps is an operating model where your desired cluster state lives in Git, and an agent running inside the cluster continuously compares live state against it and corrects the difference.

Continuously is the word doing the work there.

A normal pipeline applies a change and walks away. A GitOps controller keeps checking, every few minutes, forever. Edit a live resource by hand and it notices. Depending on your settings, it puts it back, which is either exactly what you wanted or deeply annoying, and we'll get to that.

The formal definition is four principles at opengitops.dev: declarative state, versioned and immutable, pulled automatically, continuously reconciled. Every article on this topic lists them. I'd rather spend the words on what happens after you've installed the thing.

Push versus pull

The old way: Jenkins holds a kubeconfig with wide permissions, runs kubectl apply, reaches into production from outside.

Works fine. Also means your CI server, the one executing arbitrary code from pull requests, is holding keys to production. Add clusters and you add credentials to that same place. Compromise CI and you own everything downstream of it.

Pull-based flips the direction. The agent sits inside the cluster and reaches out to Git. Nothing external holds kubectl access. Your pipeline's job ends at pushing an image and updating a tag in a repo. Much smaller blast radius when something goes wrong, and something eventually does.

What you actually get

Your audit trail turns into git log. Who changed the replica count, when, which PR approved it. This is the bit auditors care about, and it's also the bit you care about at 9 AM during a postmortem.

Rollback turns into git revert. Not a procedure. Not a runbook. The command every engineer already knows.

Disaster recovery gets dull, in the good way. Cluster gone? Spin up a new one, point the agent at the repo, make tea. I watched a team do exactly this with a non-production environment once and the whole thing took about twenty minutes. Try that when half your config only exists in somebody's bash history.

And onboarding changes shape. New engineer reads a repository instead of booking four meetings to find out how staging got that way.

What it isn't

It isn't CI. GitOps builds nothing, tests nothing, and has no opinion about code quality. It sits downstream of your pipeline. Feed it a broken artefact and it will deploy that artefact with total reliability. How CI/CD pipelines work covers the upstream side.

The Reconciliation Loop, in Detail

One change, start to finish.

Developer merges a PR bumping an image tag. The agent either polls the repo on a timer, usually every few minutes, or gets nudged by a webhook. It renders the manifests, whether that's plain YAML, Helm or Kustomize. It diffs the result against what's live. One Deployment differs. It applies the change. Kubernetes does its rolling update. The agent watches the new pods and reports health.

No kubectl. No credential handed out. One human action, which was clicking approve.

Synced and Degraded at the same time

This one confuses everybody in month one, so here it is plainly.

Argo CD shows you two statuses, and they answer different questions.

Sync status: does the cluster match Git? Synced, or OutOfSync. That's the whole question.

Health status: is the thing working? Healthy, Progressing, Degraded, Missing.

They're independent, which is why you can see Synced and Degraded together. Not a contradiction. It means the cluster contains precisely what Git asked for, and what Git asked for is broken. Manifest correct, image crashing on startup.

The reverse shows up too. OutOfSync and Healthy means somebody changed something by hand and the app is happily running the changed version. Fine today. Wrong the second the agent reconciles, or the next time the cluster gets rebuilt.

Reading which axis is red is the difference between a two-minute fix and a two-hour one.

ArgoCD vs Flux: How They Differ in Practice

Both are CNCF Graduated, which is the foundation's top maturity tier and requires a security audit, open governance and real production adoption. Flux got there on 30 November 2022. Argo, covering Argo CD, Rollouts, Workflows and Events, followed on 6 December 2022.

Neither choice is a mistake. What differs is the operating model each one assumes.

Argo CD

A standalone application with its own control plane, its own RBAC, and a web UI.

People underrate the UI. It sounds cosmetic and it changes team behaviour. A developer opens a browser, sees their application tree, clicks the pod that won't start, reads the events. No cluster credentials involved. That one capability deletes a surprising amount of Slack traffic aimed at the platform team, and it puts sync and promotion where people are already looking.

Argo CD also runs many clusters from one place, which fits a central platform team. And the surrounding ecosystem was built to fit together: Rollouts for progressive delivery, Workflows for pipelines, Events for triggers.

The cost is weight. One consultancy benchmarking both across client environments reported Argo CD using roughly twice Flux's CPU and memory during initial sync, narrowing at steady state. That tracks architecturally, since Argo CD holds a full application graph in memory. Irrelevant on a big cluster. Not irrelevant on a small edge node.

Flux

A handful of lightweight controllers running inside the cluster, driven by CRDs and a CLI. No official UI.

Different philosophy. Flux behaves more like a library than a product: each controller does one job and you compose them. That suits independent clusters and small footprints, which is why you see it at the edge, in air-gapped environments, and in heavily isolated tenancies.

There's a case study on the Flux site I keep bringing up in class. Deutsche Telekom runs around 200 Kubernetes clusters with ten full-time engineers, and plans to reach thousands without meaningfully growing the team. That's the entire Flux argument in one sentence. Fully automated operating model, nobody needing a dashboard, so why pay resources for one.

The Weaveworks thing

Somebody always asks, so let's deal with it in a paragraph.

Weaveworks created Flux, donated it to CNCF, and shut down commercial operations in early 2024. What ended: vendor support contracts from that company. What did not end: Flux's CNCF Graduated status, its governance, or its development. GitLab, which had already made Flux its recommended GitOps integration, publicly committed to supporting it, and other organisations moved in around the project.

Real event. Worth knowing. Not the death of Flux. If your procurement requires a named support vendor, check who offers one today before you commit. Otherwise this shouldn't move your decision much.

Adoption numbers, with the usual health warning

As of August 2026, Argo CD sits at roughly 23.9K GitHub stars against about 8.3K for flux2. A CNCF Argo CD user survey published in July 2025 put 97% of respondents running it in production, and CNCF end user survey data has shown Argo CD handling application delivery on around 60% of reported Kubernetes clusters.

Popularity isn't suitability. It does mean more Stack Overflow answers at 3 AM and a bigger hiring pool, which are worth something.

Choose Argo CD if… Choose Flux if…
Developers need visibility without cluster accessEverything is automated and nobody needs a dashboard
One platform team runs many clusters centrallyClusters are independent, edge, or air-gapped
You want progressive delivery visible in the same UIResource footprint has to stay minimal
You're hiring and want the bigger talent poolYour platform already has its own control plane
Your team is newer to KubernetesYour team is comfortable in CRDs and CLI

What I tell students: learn the model, not the tool. Understand reconciliation, drift and sync ordering and the other engine takes about a week to pick up.

Progressive Delivery: What It Is and Why GitOps Needs It

Your GitOps engine has finished its job the moment the new version is running. Whether that version is any good is somebody else's problem.

Progressive delivery is the somebody else. And neither Argo CD nor Flux does it alone, which surprises people. Each has a sibling project for it.

Canary

Route a slice of traffic to the new version. Say five percent. Watch the metrics. Holding? Go to twenty. Then fifty. Then everything. Not holding? Shift traffic back, stop, page someone.

The step teams skimp on is the analysis, and a canary without real analysis is just a slower deploy with more YAML in it.

At minimum, watch success rate, meaning 5xx responses as a share of requests, and latency at a high percentile rather than the average. Average latency hides everything interesting. Past that it depends what the service does. A checkout flow might watch completion rate. A queue consumer watches lag.

Set your thresholds before the rollout. During an incident everyone's judgement gets worse, and that includes the person who wrote the thresholds.

Blue-green

Two complete environments. Blue serves traffic, green gets the new version, you verify green, then switch everything across at once. Blue stays warm, so rollback is another switch rather than another deploy.

Canary gets more coverage, but blue-green wins in specific cases. Hard cutovers where having some users on the old version breaks something. Services where per-request analysis doesn't mean much. And releases involving a schema change that can't tolerate both versions running together, though that particular problem is bigger than your deployment strategy.

You pay for two environments during the transition. That's the trade.

Argo Rollouts versus Flagger

Both mature, both CNCF-aligned, both technically work with either engine. They were designed assuming different things sit above them.

Argo Rollouts swaps your Deployment for a Rollout resource that understands canary and blue-green natively. It renders in the Argo CD UI, so promotion happens where the deployment is already on screen, and manual gates are first-class. It assumes a person is above it.

Flagger leaves your Deployment alone and wraps it. Traffic shifting goes through a service mesh or ingress controller, and promotion happens automatically on metric analysis. Want a manual gate? You register a webhook that Flagger polls until your system says go. No UI, deliberately. It assumes a control loop is above it, not a finger on a button.

On Argo CD with humans promoting: Rollouts. On Flux, or any fully automated platform: Flagger.

Two traps before you wire this up

Argo CD will report a Canary resource and its primary Deployment as healthy the moment they sync, even while an analysis is actively failing and rolling the release back. It doesn't show in the app tree unless you add custom health checks. Your dashboard says green while your release is being reverted underneath you.

And Argo CD fights Flagger. Flagger mutates the pod spec as part of doing its job; Argo CD reads that as drift and corrects it. Fix is ignoreDifferences. Nobody warns you in advance, and the symptom is a deployment that thrashes while both controllers log success.

What both of them need

Something that can split traffic, usually Istio, Linkerd or an NGINX ingress controller. And a metrics source, usually Prometheus.

No metrics? Then progressive delivery is next quarter's project. Get monitoring, logging and reliability sorted first, because analysis is only as good as what it's analysing.

Scaling Beyond One Cluster

ApplicationSet

Sooner or later the same app is deployed to dev, staging and three production regions, and you're maintaining five nearly identical Application manifests that slowly drift apart through copy-paste.

ApplicationSet generates them from a template. Generators can walk a list of clusters, directories in a repo, or open pull requests, which gives you ephemeral preview environments for almost free.

It's also where a typo stops being a typo and becomes forty broken applications at once. Review generator changes more carefully than ordinary manifests.

Repo layout

Monorepo or one repo per environment. No consensus exists, both work, and the argument is mostly aesthetic.

Monorepo puts cross-environment changes in one PR and keeps tooling simple. Repo-per-environment gives cleaner access control, which matters when different teams own different environments or an auditor is going to ask who can approve production.

Pick one, write down the reason, stop relitigating it every quarter.

Sync waves and hooks

Ordering bites everyone eventually. CRDs before the resources that use them. A migration before the deployment expecting the new schema. A namespace before anything lands in it.

Argo CD does this with sync waves, which are integer annotations controlling order, plus pre-sync and post-sync hooks. Flux uses dependsOn between Kustomizations. The first time a sync fails because a CRD wasn't ready, this is the thing you're looking for.

What Breaks in Real GitOps Setups

Tutorials stop at "Synced and Healthy". Here's the rest of it.

Secrets, which is where everyone gets stuck

Git is the source of truth. Plaintext secrets must never live in Git. Those two sentences are the entire problem, and every team meets it in week one.

Four answers, all legitimate.

Sealed Secrets. Encrypt with the kubeseal CLI against a public key; only the in-cluster controller can decrypt. The encrypted object is safe to commit. Conceptually clean, no external dependency, genuinely everything lives in Git. The friction is operational: every secret change means running a CLI with controller access, which gets awkward in multi-tenant clusters and tedious once you have fifty secrets.

SOPS. Encrypts values in place inside YAML or JSON using age or a cloud KMS. More flexible on backends and formats. Key management becomes yours, which is fine if you already run KMS properly and a problem if you don't.

External Secrets Operator. Only the declaration goes in Git. You commit an ExternalSecret saying "fetch this key from this store, create a Secret named X". The value stays in Vault, AWS Secrets Manager, Azure Key Vault. This has become the default for most Kubernetes teams, and I think rightly: rotation happens in the vault, Git never holds the value, and it works with everything that expects an ordinary Secret.

Secrets Store CSI driver. Values never reach etcd, which is a genuine security win. The catch is that the secret only exists while a pod runs with the volume mounted, which breaks a lot of Helm charts and controllers expecting a named Secret. Specialist choice, not a general answer.

Approach Value lives in Best when Main friction
Sealed SecretsGit, encryptedEverything must live in the repoCLI-heavy, manual per secret
SOPSGit, encryptedYou already run KMS wellKey management is on you
External Secrets OperatorExternal vaultMost teams, most of the timeAnother controller plus a vault to run
CSI driverVault, mounted at runtimeSecrets must stay out of etcdPod-centric; poor chart compatibility

Want a default? ESO with a central vault. Add SOPS or Sealed Secrets only when something genuinely has to sit in the repo. Separately, and more important than any of this: workload identity like IRSA on AWS beats mounting long-lived cloud credentials as Secrets at all. More in DevSecOps basics.

Drift you want versus drift you don't

New teams treat every OutOfSync as a failure. Most aren't.

Cert-manager renews a certificate and the Secret changes. ESO refreshes a rotated credential. An HPA adjusts replicas under load. All three are the system working exactly as designed, and all three light up as drift.

The drift worth caring about is different: a manual kubectl edit, a mutating webhook rewriting fields, a misconfigured operator fighting you for ownership.

ignoreDifferences exists for precisely this. Tell Argo CD which fields to stop watching, at resource or field level, and the noise goes. Teams that skip this end up with a permanently yellow dashboard, which functions the same as no dashboard at all.

Self-heal and the 11 PM problem

Enable self-heal and the agent doesn't just spot drift, it reverts it. On its own.

Now go back to the scene at the top of this article. Production on fire, engineer patches a live resource, alerts stop. Three minutes later the agent notices the cluster no longer matches Git and puts it back. Alerts return. Engineer patches again. Repeat until somebody works out what's happening.

I have watched this happen. Nobody enjoyed it.

The fix isn't disabling self-heal, and it isn't banning emergency access either. It's designing the escape hatch properly. Know how to suspend an application or turn off auto-sync for it, put that in the runbook where the on-call person will find it at midnight, and require a follow-up PR the next morning. Emergency procedures need designing. Forbid them and they just become undocumented.

Two engines, one resource

Never let two controllers actively reconcile the same object. They fight over ownership, and the symptom is a resource flipping between two states every few seconds while both controllers cheerfully report success.

Happens during migrations between Argo CD and Flux. Also happens when a Helm operator and a GitOps engine both think they own a release. Whatever the situation, suspend one side per resource before the other takes over.

The pull request bottleneck

Every change becomes a PR. That's the design. It's also why teams quietly keep a kubectl escape hatch and stop mentioning it in standups.

Be honest about this when you design the workflow. If a one-line config change needs two approvals and forty minutes, people route around it, and once they do, your repo stops reflecting reality, which was the one thing you were trying to fix.

Repo sprawl

Eighteen months in, you have a manifest repo with several thousand YAML files, most of them nearly identical, and pull requests approved at a glance because reading a 400-line diff of generated Kustomize output is nobody's idea of a good Friday.

Kustomize bases, Helm charts and ApplicationSet generators all reduce it. None eliminate it. Budget time for repo maintenance the way you budget for dependency upgrades, or the repo will quietly become the thing everyone avoids.

A Realistic Starter Path

If you're learning rather than rolling this out for a company, this is the order I'd use.

One cluster, one app, sync by hand. Install Argo CD, point it at a repo holding a plain Deployment and Service, click sync yourself. Watch what it does. Build the mental model before you automate anything.

Turn on auto-sync and self-heal, then break things deliberately. Change the replica count with kubectl and watch it revert. Delete a pod. Delete the whole Deployment. Fifteen minutes of this teaches more than any article does, including this one.

Add Helm or Kustomize. Almost every real repo uses one. Find out how your engine renders them and where values actually come from, because that's where half of all confusing behaviour originates.

Then secrets. ESO against whatever vault you have, or Sealed Secrets if you're on a laptop cluster with nothing else running. Tutorials skip this. Jobs don't.

Then multi-cluster, if you have a second cluster. ApplicationSet, one template.

Progressive delivery last, and only once Prometheus works. Fifth or sixth, not first. A canary with no metric analysis is theatre. Get metrics running, decide what healthy means as a number for your service, then add Rollouts or Flagger.

The temptation is to start with progressive delivery because it demos beautifully. Resist it. If you are also weighing how much of this an AI assistant can do for you, AI for DevOps explained covers what the research actually found. For a broader sequence covering everything around this, see the 2026 DevOps roadmap. If you'd rather do it in a cohort with someone reviewing your work, that's our DevOps certification course.

Where This Leaves You

GitOps is a reconciliation model rather than a product. Once that lands, the tool question gets much less exciting: both engines are good, and the answer depends mostly on whether humans need a dashboard and how many clusters you're running.

Progressive delivery is a separate layer on top, and it needs working metrics before it needs anything else.

The work that will actually eat your time isn't the install. It's secrets, drift configuration, and building an emergency path that doesn't quietly undermine the model. Nobody writes tutorials about those three, which is exactly why they're worth learning properly.

Which, roughly, is what I tell the student who asked why Jenkins wasn't enough.

Frequently Asked Questions

What is GitOps in simple terms?

Your desired cluster state lives in Git, and an agent in the cluster continuously checks live state against it and fixes differences. Deploying becomes merging a PR. Rolling back becomes reverting one.

Is GitOps the same as CI/CD?

No. It's part of the CD half. Your CI still builds, tests and pushes images. GitOps takes over from there and keeps the cluster matching Git. Complementary, not alternatives.

What's the difference between GitOps and DevOps?

DevOps is a broad set of practices and culture around building and running software. GitOps is one specific technique inside it, for managing cluster state. You can do DevOps without GitOps. GitOps without the surrounding practices usually goes badly.

ArgoCD or Flux, which should I learn first?

Argo CD for most people. Bigger job market, and the UI makes the concepts visible while you're still learning them. Once reconciliation makes sense, Flux takes about a week.

Is Flux still maintained after Weaveworks shut down?

Yes. Weaveworks closed commercial operations in early 2024, but Flux is CNCF Graduated with independent governance, and organisations including GitLab committed publicly to supporting it. What disappeared was support contracts from that one company.

Do I need Kubernetes to do GitOps?

In practice, mostly. The principles apply anywhere you have declarative state and a reconciler, and Terraform workflows borrow the same ideas. But the mature tooling is Kubernetes-native.

How do you handle secrets in GitOps?

Four options. Sealed Secrets and SOPS encrypt values so they can live in Git. External Secrets Operator keeps values in a vault and commits only the declaration. The CSI driver mounts them at runtime without touching etcd. ESO plus a vault is the common default.

What is drift detection, and should I turn on self-heal?

Drift is any gap between live state and Git. Self-heal reverts it automatically. Turn it on, but configure ignoreDifferences for legitimate drift like cert rotation and HPA scaling, and make sure on-call knows how to suspend sync during an incident.

What is progressive delivery?

Releasing gradually while watching metrics instead of switching everything at once. Canary and blue-green are the two patterns. It's a separate layer from GitOps: Argo Rollouts pairs with Argo CD, Flagger with Flux.

Canary or blue-green, which should I use?

Canary for most stateless HTTP services, since it limits exposure and tests against real traffic. Blue-green when a partial rollout would break something, when per-request analysis isn't meaningful, or when you need instant full cutover and rollback.

Do I need a service mesh for canary deployments?

Not necessarily. You need something that can split traffic by weight, and an NGINX ingress controller manages that. A mesh gives finer control and better telemetry. Start with whatever you already run.

Is GitOps overkill for a small team?

For one cluster and one app, maybe. It pays off with more environments, more people touching the cluster, or any audit requirement. Though a small team can have Argo CD doing useful work in an afternoon, and these habits are far easier to build early than retrofit later.

Related Topics:GitOpsGitOps explainedArgoCDArgo CD vs FluxFlux CDprogressive deliverycanary deploymentblue-green deployment

Ready to Start Your DevOps Career?

Join our comprehensive DevOps + GenAI course with hands-on projects, live mentorship, and placement support