What is GitOps? Git-Driven Infrastructure Explained
Most CI/CD pipelines work by pushing -- a pipeline authenticates to your cluster and runs kubectl apply or helm upgrade when a build succeeds. This works, but it creates problems at scale: pipeline credentials with cluster access are a security risk, there is no single record of what is actually deployed, and manual hotfixes applied directly to the cluster silently diverge from what is in your pipeline scripts.
GitOps addresses all of these by inverting the model. Instead of a pipeline pushing changes to the cluster, an operator running inside the cluster continuously pulls from Git and reconciles the cluster state toward what the repository says it should be.
The term GitOps was coined by Alexis Richardson (founder of Weaveworks) in 2017. It has since become a widely adopted pattern for Kubernetes deployments, backed by the OpenGitOps working group under the CNCF.
The Four Principles of GitOps
The OpenGitOps specification defines GitOps around four principles:
Declarative configuration: the desired state of your system is expressed declaratively (Kubernetes YAML, Helm charts, Kustomize overlays) -- not as imperative scripts.
Version controlled and immutable: the desired state is stored in Git. Every change is a commit. The history is auditable and reversible.
Automatically pulled: software agents (ArgoCD, Flux) automatically pull the desired state from Git and apply it to the system.
Continuously reconciled: the agents continuously compare desired state to actual state and correct any drift. A manual kubectl edit that is not in Git gets reverted on the next sync.
The Pull Model in Practice
Here is the difference between a push pipeline and a GitOps pull setup:
Push (traditional):
- CI builds and tests the new image
- CI pushes the image to ECR
- CI authenticates to the cluster and runs
helm upgrade myapp --set image.tag=v1.4.2 - CI pipeline has cluster credentials stored as secrets
GitOps pull:
- CI builds and tests the new image, pushes to ECR
- CI opens a PR updating
values.yamltoimage.tag: v1.4.2 - PR is reviewed and merged
- ArgoCD detects the commit, compares cluster state to the repo, applies the diff
- No pipeline ever touches the cluster directly
The cluster credentials never leave the cluster. The change history lives in Git. The diff between what's deployed and what the repo says is always visible in the ArgoCD UI.
A GitOps Repository Structure
A common pattern is separating application code from deployment configuration into two repositories (the "app of apps" pattern):
infra-gitops-repo/
apps/
production/
api-server/
values.yaml # image.tag: v1.4.2
worker/
values.yaml
staging/
api-server/
values.yaml # image.tag: v1.5.0-rc1
clusters/
production/
apps.yaml # ArgoCD Application pointing at apps/production/
staging/
apps.yaml
ArgoCD's Application manifest for the API server might look like this:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-server
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/mycompany/infra-gitops-repo
targetRevision: main
path: apps/production/api-server
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Revert manual changes made outside Git
With selfHeal: true, any manual change to the cluster -- a direct kubectl edit, an emergency patch -- is automatically reverted to match Git. This is the defining behavior of a true GitOps setup.
GitOps vs. Traditional CI/CD
GitOps does not replace CI -- it replaces the deployment step of CD. CI still handles testing, building, and image publishing. The handoff point is a commit to the gitops repository that updates the image tag or config values. From there, the GitOps operator takes over.
The practical benefit becomes clear when something goes wrong. Rolling back a traditional deployment means re-running a pipeline with an old image tag, or running helm rollback. Rolling back with GitOps is git revert -- a standard developer operation that produces an auditable commit and triggers the normal reconciliation flow.
Choosing Between ArgoCD and Flux
ArgoCD has a polished web UI that makes it easy to see sync status, diff deployed state vs. Git, and manually trigger syncs. It is the more popular choice for teams that want visibility. Helm integration is first-class.
Flux v2 is fully Kubernetes-native (it uses CRDs for everything) and is more composable. It is a better fit if you prefer a pure GitOps-from-configuration approach with no UI dependency. Both are CNCF graduated projects.
For more on how GitOps fits into the broader picture, see what is DevOps and the full Kubernetes guide for beginners.
