Skip to content

What GitOps Is

CI/CD gets a change built, tested, and out the door. GitOps asks a narrower question about that last step: once the change is ready to deploy, what actually decides — moment to moment — what's running in production? GitOps's answer: git does, and something else's whole job is keeping the cluster matching it.

TL;DR — in 30 seconds:

  • GitOps stores the desired state of infrastructure and deployments declaratively, in git — a git repo is the one place that says what should be running.
  • An operator (ArgoCD, FluxCD) runs inside the target environment and continuously reconciles the live state to match what git declares — nobody applies changes by hand.
  • Because git is authoritative, every deploy, every rollback, and every audit question ("what was running on Tuesday?") has one, versioned, reviewable answer: whatever git said at that point.

1. Declarative state, one source of truth

flowchart LR
    A["Engineer merges<br/>a change to git"] --> B["Git repo<br/>desired state"]
    B --> C["Operator reconciles"]
    C --> D["Live environment<br/>now matches git"]

A declarative description says what the end state should look like (e.g. "3 replicas of this image, this config") rather than how to get there step by step. Storing that description in git gives you, for free, everything git already does well: version history, code review via pull request, a diff of exactly what changed, and a single place to look for "what's supposed to be running right now."

Compare this to a world where the desired state lives only in whoever last ran a deploy command's memory, or scattered across a pipeline's imperative steps — there's no single artifact you can point to and say "this is authoritative."

2. Reconciliation, not one-time apply

The key shift from "we use git in our deploy process" to actual GitOps is continuous reconciliation: an operator doesn't apply the desired state once and walk away. It keeps comparing the live state to git, on a loop, for as long as it runs. That's what makes drift detection possible (covered on page 4) — the operator would notice if the live state stopped matching git, even if nothing new was ever merged.

Common gotchas

  • Thinking "our pipeline reads from a git repo" is the same as GitOps. Fix: the defining trait is a continuously reconciling operator, not merely that a git repo is somewhere in the picture — a one-shot git pull && kubectl apply in a CI job is not GitOps.
  • Treating the declarative manifests as documentation rather than the actual source of truth. Fix: if the live environment can diverge from what's in git without git being the thing that wins, git isn't actually authoritative yet.
Check yourself — a team already stores their Kubernetes manifests in a git repo and runs kubectl apply -f . from a CI job on every merge. Is this GitOps?

Not fully. Storing manifests in git and applying them on merge is a good start, but GitOps requires an operator that continuously reconciles — comparing live state to git on an ongoing basis, not just applying once per merge. Without continuous reconciliation, a manual change to the live cluster between merges would go completely undetected.

Done when: you can explain, in one sentence, why "git repo plus a one-time apply" is not the same guarantee as continuous reconciliation.