· Charlie Holland · DevOps · 10 min read
GitOps Before It Had a Name
We were using FluxCD to reconcile Kubernetes state from git before anyone called it GitOps. The tooling was raw, the wins were real, and the company that coined the term went bankrupt. Make of that what you will.
Before GitOps was a conference talk, a vendor pitch, or a CNCF landscape category, it was just an idea: what if git was the source of truth for your infrastructure, and a controller in the cluster made reality match what was in the repo?
That’s it. That’s the whole thing. It’s not complicated. It’s not revolutionary. It’s version control applied to infrastructure state, with a reconciliation loop to enforce it. If you’ve ever used a config management tool — Puppet, Chef, Ansible — the concept is familiar. Desired state, convergence, drift detection. GitOps just moved the desired state into git and the convergence loop into Kubernetes.
I was doing this before the term existed, working at a Big Four consultancy and later at a major media conglomerate. We were using FluxCD — back when it was just called Flux, maintained by a small company called Weaveworks that most people hadn’t heard of. The tooling was basic. The documentation was thin. The community was small. But the principle was sound, and when it worked, it was genuinely brilliant.
What it was actually like
Let me be clear about the state of play in 2018-2019. Kubernetes was mainstream enough that serious organisations were running production workloads on it, but the operational tooling around it was still catching up. Helm existed but was clunky (Tiller, anyone?). Kustomize was emerging. CI/CD pipelines were the primary deployment mechanism for most teams: build, push, deploy, hope.
The problem with CI/CD-driven deployments was drift. Your pipeline would deploy version X of a manifest to the cluster. Then someone would kubectl edit something in production to fix an incident. Then someone else would kubectl apply a modified manifest from their laptop because the pipeline was slow and they needed a quick fix. Within a few weeks, the state of the cluster and the state of your git repo had diverged. Nobody knew which was correct. Deployments became nerve-wracking because you didn’t know what you were deploying on top of.
Flux solved this. You pointed it at a git repo. It watched for changes. When the repo changed, it applied the new state to the cluster. When the cluster drifted from the repo — because someone did a manual kubectl change — Flux reverted it. Git was the truth. The cluster was a reflection of that truth. If you wanted to change something, you changed it in git. End of story.
Simple. Powerful. And in 2018, rough as a badger’s backside.
The rough edges
FluxCD v1 was not a polished product. It worked, but it had all the hallmarks of a tool built by infrastructure engineers for infrastructure engineers, without much thought for developer experience or operational niceties.
Image update automation was the killer feature and the biggest headache. Flux could watch a container registry, detect when a new image tag appeared, and automatically update the manifests in git to reference the new tag. In theory, this closed the loop beautifully: push code, CI builds an image, Flux detects it, updates git, reconciles the cluster. In practice, the image update policies were fiddly to configure, the tag filtering was limited, and the automated commits it made to your git repo were ugly — machine-generated diffs that cluttered your git history and made it harder to understand what had actually changed and why.
The reconciliation loop was slow and opaque. Flux polled your git repo on an interval. If you pushed a change and waited, it might take a few minutes before anything happened. There was no good way to see what Flux was thinking, what it was about to do, or why it hadn’t done something you expected. The logs were there, but they weren’t designed for humans trying to understand “why hasn’t my deployment updated yet?” When you’re used to a CI pipeline that gives you a nice green tick, staring at a controller’s logs and trying to decode its intentions is a step backwards in developer experience.
Multi-tenancy was basically nonexistent. Flux v1 operated at the cluster level. One Flux instance, one git repo, one reconciliation loop. If you had multiple teams sharing a cluster (and most organisations did), you either gave everyone access to one repo — which was a governance nightmare — or you tried to carve up the repo with directory conventions and hope nobody stepped on each other’s manifests. It was workable for small teams. It was a headache for anything larger.
Secrets management was “figure it out yourself.” Flux reconciled manifests from git. Secrets are manifests. You can’t put secrets in git. So you needed a separate solution: Sealed Secrets, SOPS, external secret stores, or just manually managing secrets outside the GitOps flow and hoping nobody forgot. The tooling existed but wasn’t integrated. Every team I worked with had a different approach to this, and most of them had at least one near-miss where someone nearly committed a plaintext secret to the repo.
The wins
Despite all of that, when it worked, it changed how teams operated.
Audit trail. Every change to the cluster was a git commit. Who changed what, when, and why — it was all there. For organisations with compliance requirements (and both the consultancy and the media company had plenty), this was transformative. No more “who kubectl apply’d that?” No more forensic log diving to figure out what changed. The git history was the changelog.
Rollback. If a deployment broke something, you reverted the git commit. Flux saw the revert, reconciled the cluster to the previous state. Done. No panicked kubectl rollout undo commands. No searching for the previous working manifest. Just git revert and wait. The confidence this gave teams — especially less experienced engineers — was significant.
Drift elimination. This was the big one. The moment Flux was running, manual cluster changes became impossible. Or rather, they became temporary — Flux would revert them on the next reconciliation cycle. This killed an entire category of production incidents caused by drift. It also killed the “works on my cluster” problem, because everyone’s cluster matched git.
Deployments became pull requests. Want to deploy a new version? Open a PR. Want to change a configuration? Open a PR. Want to scale a deployment? Open a PR. Code review, approval, merge, automatic deployment. The deployment process inherited all the benefits of code review — a second pair of eyes, a discussion thread, a clear approval record. For teams that were used to “run the deploy script and pray,” this was a revelation.
ArgoCD and the tooling wars
While we were wrestling with Flux v1’s rough edges, ArgoCD was emerging as an alternative. Built by Intuit and later donated to the CNCF, ArgoCD took a different approach: it had a UI. A proper, visual, “here’s what’s deployed and here’s what’s about to change” dashboard. For teams that found Flux’s command-line-only interface intimidating, ArgoCD was immediately more approachable.
ArgoCD also had a more opinionated model around applications and projects, which made multi-tenancy easier. And it supported Helm charts as a first-class concept, which Flux v1 handled through a separate component (the Helm Operator) that was another piece of infrastructure to manage.
I used both. I still use both. They’re both fine. The religious wars between Flux and ArgoCD partisans were — and remain — dull as ditch water. They’re different implementations of the same principle. Pick the one that fits your team and move on. The principle matters more than the tool.
What was genuinely interesting was watching the ecosystem coalesce around the idea. In 2018, explaining GitOps to a team required a whiteboard session and some patience. By 2020, it was in every vendor’s marketing material, every cloud provider’s documentation, and every DevOps conference programme. The idea won. Comprehensively.
GitOps as a marketing term
And that’s where things went sideways.
The moment GitOps became a recognised term, every vendor with a deployment tool slapped it on their product. CI/CD tools that pushed deployments to clusters called themselves “GitOps.” Terraform Cloud called itself GitOps because it triggered runs from git commits. Platform-as-a-service products called themselves GitOps because they deployed from a git repo. None of these were GitOps in the original sense — they didn’t have a reconciliation loop, they didn’t enforce convergence, they were push-based, not pull-based.
The term lost its meaning. “GitOps” went from describing a specific operational pattern — pull-based reconciliation of desired state from git — to meaning “anything that involves git and deployments.” Which is everything. So it means nothing.
This isn’t a new phenomenon. “DevOps” went through the same cycle. “Cloud native” went through the same cycle. “Agile” went through the same cycle twenty years ago. An idea starts with a clear, specific meaning. It proves valuable. It gains traction. Vendors co-opt the term. The term expands to encompass everything. The original meaning is lost. The people who understood the original idea shake their heads and carry on doing the actual work while the marketing machine moves on to the next term.
Weaveworks: the company that coined the term and couldn’t survive on it
Here’s the part that stings. Weaveworks — the company that created Flux, coined the term “GitOps,” and arguably did more than anyone to establish the practice — went bankrupt in February 2024. Shut up shop. Gone.
They had real technology. Real adoption. Real mindshare. Flux v2 was a genuinely excellent piece of software — a complete rewrite that addressed virtually every rough edge from v1. The CNCF graduated it. The community was healthy. And the company behind it couldn’t make money.
The narrative you’ll hear is that it’s hard to monetise open-source infrastructure tooling, and that’s true. But I think there’s a sharper lesson: coining a term and building the open-source reference implementation doesn’t translate to commercial success when every vendor in the ecosystem adopts your term and builds their own version. Weaveworks made GitOps a thing. AWS, Azure, GCP, GitLab, GitHub, Harness, and a dozen others made money from it. Weaveworks got the credit and the bankruptcy.
There’s something deeply wrong with how the industry treats the people and companies that create foundational ideas and tools. But that’s a longer rant for another day.
What GitOps actually is, in 2019 terms
Strip away the marketing. Strip away the vendor pitches and the conference talks and the certification programmes. GitOps is this:
- Desired state in git. Your infrastructure and application manifests live in a git repository. That repository is the single source of truth.
- A reconciliation loop. A controller (Flux, ArgoCD, whatever) watches the repo and continuously ensures the cluster matches what’s in git.
- Pull, not push. The cluster pulls its state from git. You don’t push deployments to the cluster. This is a security boundary — the cluster has read access to git, but your CI pipeline doesn’t need write access to the cluster.
- Convergence. If the cluster drifts from the desired state — manual changes, failed deployments, whatever — the controller brings it back. Automatically.
That’s it. If your deployment tool doesn’t do all four of those things, it’s not GitOps. It might be perfectly good, but it’s not GitOps. Call it what it actually is.
I was doing this in 2018 with raw FluxCD on a Kubernetes cluster, writing YAML by hand, waiting for the reconciliation loop, and swearing at the image update automation. It wasn’t glamorous. It wasn’t easy. But it was correct — the principle was right then, and it’s right now.
The tooling has improved enormously. Flux v2 is excellent. ArgoCD is mature. The ecosystem supports Helm, Kustomize, OCI artifacts, multi-tenancy, progressive delivery, and a dozen other things we couldn’t do in 2018. The rough edges have been sanded down.
But the principle hasn’t changed. Git is the truth. The cluster converges on it. Everything else is implementation detail.
We were doing this before it had a name. The name helped it spread. The marketing nearly killed the meaning. The company that started it all is gone. And the idea — the actual, useful, practical idea — endures.
That’s usually how it works with good ideas. They outlive the hype, the companies, and the marketing. The builders carry on. The buzzword moves on. The work remains.
