Picture the classic deployment story. A pipeline builds an image, then runs a script with cluster credentials that pushes the new version into production. It works — until someone makes a "quick fix" directly on the cluster, the live state quietly drifts away from what's in the repository, and nobody can say with confidence what's actually running.
GitOps flips that model. Instead of a pipeline pushing changes in, an agent inside the environment continuously pulls the desired state from Git and makes reality match it. The repository stops being a record of intentions and becomes the control surface.
The four principles
The OpenGitOps working group, part of the CNCF community, describes GitOps with four principles:
- Declarative. The desired state of the system is expressed declaratively — Kubernetes manifests, Helm charts, Kustomize overlays.
- Versioned and immutable. That desired state lives in a system that keeps a full, immutable history. In practice, Git.
- Pulled automatically. Software agents automatically pull the desired state from the source.
- Continuously reconciled. Agents continuously compare actual to desired state and correct differences.
Everything else follows from those four.
What it changes day to day
A deployment becomes a pull request. You change the image tag in a manifest, open a PR, get it reviewed, and merge. Within moments the agent notices the new commit and applies it. Rolling back is git revert. The approval process, the audit history, and the access control are all the ones your team already uses for code.
Security improves too: since the cluster pulls changes, your CI system no longer needs credentials to reach production. That removes a high-value target we discuss in our post on securing GitHub Actions secrets, OIDC, and least privilege.
A typical repo layout
platform-config/
├── apps/
│ └── web/
│ ├── base/ # shared manifests
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── overlays/
│ ├── staging/ # staging-specific patches
│ └── prod/ # production-specific patches
└── clusters/
└── prod/ # which apps this cluster runsSeparating application source code from environment configuration is a common pattern: CI builds and publishes an image from the app repo, then updates the image tag in the config repo, and the GitOps agent does the rest.
Argo CD and Flux: the two leading tools
Both are CNCF graduated projects and both do the core job well. The differences are more about philosophy and experience than capability.
| Argo CD | Flux | |
|---|---|---|
| Style | Application-centric, with a rich web UI | Toolkit of composable controllers, CLI- and Git-first |
| Visibility | Visual app tree, sync status, diffs | Status via CLI, notifications, and Kubernetes resources |
| Config model | Application resources point at repo paths | GitRepository + Kustomization / HelmRelease resources |
| Good fit for | Teams that value a UI and a central view across many apps | Teams that prefer building from small primitives and automating everything as config |
Neither is universally better. Teams that want developers to see what's deployed often lean toward Argo CD; teams that prefer a lean, composable toolkit often choose Flux. Both are reasonable starting points.
A minimal Argo CD application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/platform-config.git
targetRevision: main
path: apps/web/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: web
syncPolicy:
automated:
prune: true # remove resources deleted from Git
selfHeal: true # revert manual changes on the clusterselfHeal is the GitOps principle made concrete: if someone edits a live resource by hand, the agent puts it back.
The catches worth knowing
- Secrets don't belong in plain Git. You need a strategy — sealed or encrypted secrets, or an external secrets operator that pulls from a secrets manager.
- It's more than YAML in a repo. Structure, promotion between environments, and ownership need deliberate design, or you inherit a sprawling config repo nobody understands.
- Feedback can feel slower than a direct push, because reconciliation is asynchronous. Good notifications and status visibility matter.
- It governs the cluster, not your whole delivery pipeline. You still need CI for building, testing, and scanning images.
Is GitOps right for you?
It tends to pay off when you run Kubernetes at some scale, have multiple environments or clusters, need strong audit trails, or struggle with configuration drift. For a single small app on a managed platform, simpler deployment may be perfectly fine. Adopt it for the problems it solves, not because it's fashionable.
To go deeper, our GitOps Fundamentals course takes teams through repo design, tooling, and promotion workflows hands-on, and our CI/CD Pipeline Design and Implementation course covers the build side that feeds it. New to Kubernetes itself? Start with Kubernetes 101.
