GitOps
GitOps is an operating model where Git is the source of truth for what's running: the desired state of your infrastructure and applications lives in a repository, and an in-cluster agent continuously reconciles reality to match it. Deploying means merging a pull request; rolling back means reverting a commit; auditing means reading git log.
The model grew up with Kubernetes — whose declarative API makes "converge to this YAML" natural — and tools like Argo CD and Flux made it the default deployment pattern for serious Kubernetes shops. The shift it represents: CI systems stop pushing changes into clusters; clusters pull their configuration from Git.
TL;DR
- Four principles: state is declarative, stored versioned in Git, applied automatically by agents, and continuously reconciled (drift gets corrected, not just detected).
- Pull beats push: an in-cluster operator (Argo CD / Flux) watches the repo and syncs — CI never needs cluster credentials, and clusters behind firewalls still deploy.
- The pipeline split: CI builds and tests an image; "deploy" is a commit that bumps the image tag in the config repo.
- Reconciliation is the superpower: a manual
kubectl editgets reverted by the agent — the cluster cannot silently drift from what Git says. - Keep application code and deployment config in separate repos (or at least separate paths) — config commits shouldn't trigger app CI and vice versa.
- Secrets need special handling: sealed/encrypted in Git (SOPS, Sealed Secrets) or referenced from a vault (External Secrets Operator) — never plaintext.
How It Works
Compare push-based CD: the pipeline runs kubectl apply with cluster-admin credentials stored in CI. That works, but the GitOps inversion buys real properties — CI compromise no longer equals cluster compromise, disaster recovery is "point a fresh cluster at the repo," and the deployed state is always inspectable and diffable.
Quick Example
An Argo CD Application — the object that says "keep this cluster path in sync with this repo path":
From here, deployment activity is entirely Git activity:
Core Concepts
Desired State, Declaratively
Everything the agent manages is declarative config — raw Kubernetes YAML, Kustomize overlays (base + per-environment patches), or Helm charts with per-env values. The agent's loop is always the same: render → diff against cluster → apply the difference → report sync/health status.
This extends beyond apps: cluster add-ons (ingress, cert-manager, monitoring) managed the same way make clusters reproducible, and tools like Crossplane extend the model to cloud resources — converging with what Terraform does push-style (see Infrastructure as Code).
Repo Structure and Environment Promotion
The dominant layout — one config repo, environments as directories, promotion as commits:
💡 Prefer directories over long-lived branches for environments.
stagingandproductionbranches invite merge drift and cherry-pick archaeology; directories keep every environment visible in one tree and promotion reviewable in one diff.
Promotion staging → production is a PR that copies the tested tag forward — reviewable, auditable, revertible. Image-update automation (Argo CD Image Updater, Flux image automation) can write those commits for you on new image pushes.
Argo CD vs Flux
Both are CNCF-graduated and excellent; teams wanting a visual control plane usually pick Argo CD, platform teams composing their own tooling often pick Flux. Argo Rollouts adds progressive delivery (automated canary analysis against metrics) on either.
Secrets in a GitOps World
Plaintext secrets in Git is the obvious trap. The two sanctioned patterns:
- Encrypted in the repo: Mozilla SOPS (age/KMS-encrypted YAML, decrypted by the agent) or Sealed Secrets (cluster-key encryption). State stays fully in Git.
- Referenced, not stored: External Secrets Operator syncs from Vault/AWS/GCP secret managers; Git holds only the reference. Rotation happens in the manager, not via commits.
Either way, the config repo's access control becomes security-critical — it is your deploy credential now. See Secrets Management.
Common Mistakes
CI Still Pushing With kubectl
Half-adopted GitOps — agent installed, but pipelines also apply manifests directly — produces fights between CI and the reconciler. Pick one writer for cluster state: the agent. CI's contract ends at "image pushed, config committed."
App Code and Config in One Repo
Every config bump triggers app CI; every app change dirties deploy history; the agent watches a repo that's mostly noise. Separate them (or at minimum, separate paths with CI path-filters and agent path-scoping).
Ignoring Drift Reports
selfHeal: false with nobody watching the out-of-sync dashboard means Git has quietly stopped being the truth. Either self-heal, or treat drift alerts as pages — the middle ground is where confidence goes to die.
Emergency Changes That Never Land Back in Git
The 3 a.m. kubectl scale saved the night; the agent reverted it at 3:05. Teams need a break-glass procedure that includes the follow-up commit — or use the agent's own tooling (sync windows, manual overrides) so the exception is visible.
Unbounded Blast Radius
One repo, one branch, auto-sync to twenty clusters: a bad merge is a fleet-wide incident. Stage rollouts across clusters (ApplicationSets with progressive waves), protect the repo with required reviews, and make production promotion an explicit PR.
FAQ
Is GitOps only for Kubernetes?
It's most natural there, because Kubernetes is a declarative, reconciling system already. The principles (declarative state in Git, automated convergence) extend outward — Terraform-plus-Atlantis PR workflows are GitOps-shaped for cloud infra, and Crossplane brings cloud resources under the same in-cluster reconcilers. But the mature tooling assumes Kubernetes.
GitOps vs regular CI/CD?
Not versus — a re-division of labor. CI still builds, tests, and publishes images. What changes is the CD half: instead of the pipeline pushing into environments, environments pull from Git. You keep your CI system; your deploy job shrinks to a commit.
How do rollbacks work?
git revert of the offending config commit; the agent converges backward like any other change. Caveat: databases don't revert with your YAML — schema migrations still need their own forward/backward discipline (see Database Migrations), and Argo Rollouts-style progressive delivery reduces how often you need rollbacks at all.
Doesn't this make Git a single point of failure?
The cluster keeps running on last-known state if Git is down — you just can't change anything (which, during a Git outage, may be a feature). The real concentration is authorization: repo write access now equals deploy access, so branch protection, reviews, and signed commits stop being hygiene and become access control.
Where do I start?
One cluster, one non-critical service: install Argo CD, create a config repo with a staging overlay, point an Application at it, and do two weeks of deploys by PR. The workflow sells itself (or surfaces your org's real blockers — usually secrets and repo topology) faster than any evaluation doc.
Related Topics
- Kubernetes — The platform GitOps grew up on
- CI/CD — The build half of the pipeline
- Infrastructure as Code — The same philosophy for cloud resources
- Terraform — Push-model IaC counterpart
- Helm — Chart-based config the agents render
- Deployment Strategies — Canary and blue-green via Argo Rollouts
- Secrets Management — SOPS, Sealed Secrets, External Secrets