GitOps Explained — What It Is and Why Teams Adopt It
GitOps in plain words — what it actually is, the workflow it enables, and a hands-on demo using Argo CD on a local Kubernetes cluster.
Key takeaways
GitOps in plain words — what it actually is, the workflow it enables, and a hands-on demo using Argo CD on a local Kubernetes cluster.
On this page
GitOps Explained: What It Is and Why Teams Adopt It#
By the end of this post you'll have a clear definition of GitOps, a working mental model of how it changes deployment workflows, and a hands-on demo where you push a commit and watch a Kubernetes cluster reconcile to match. About 30 minutes.
You'll need: Docker installed, plus kubectl, kind, and a free GitHub account.
What GitOps is, in one paragraph#
GitOps is a model for managing infrastructure where a Git repository is the source of truth for the desired state, and an automated agent continuously reconciles the live system to match. To change something, you commit to the repo. To roll back, you git revert. To audit who changed what when, you read the git log.
Three concrete properties fall out of this:
- No clicking around. All cluster state lives in YAML files in a repo, not in a console.
- Drift is detected automatically. If someone changes a Deployment manually, the agent notices and reverts (or alerts) — Git is supposed to be the truth.
- Rollbacks are trivial.
git revertrolls back the change; the agent picks it up and reverts the cluster.
That's GitOps. The rest is implementation.
Why teams adopt it#
A few problems GitOps solves:
- Drift between Git and cluster. Engineers
kubectl editduring an incident to fix something. Next CI deploy reverts their change — sometimes during the next incident. - No audit trail. "Who changed this Deployment last and why?" requires cross-referencing CI logs, kubectl audit, and Git. None alone is enough.
- Painful rollbacks. Rolling back means running an old version of CI, which sometimes doesn't work because the CI tooling moved on.
GitOps gives you: cluster state always matches Git, every change is a Git commit (audit trail by default), rollback is git revert + sync. The trade is operational discipline (no more click-fixes) and an extra moving piece (the agent).
The two big GitOps tools#
Both watch a Git repo and reconcile a Kubernetes cluster:
- Argo CD — UI-driven, easier for newcomers, the more popular choice
- Flux — CLI-first, lighter, deeper integration with Kubernetes API
We'll use Argo CD. The model is identical for Flux.
Step 1: Spin up a local cluster#
kind create cluster --name gitops-tutorial
kubectl get nodes # should show one Ready node
Step 2: Install Argo CD#
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
This drops Argo CD's deployments and services into the argocd namespace. Wait ~60 seconds for everything to start:
kubectl get pods -n argocd
You should see ~7 pods, all Running once they're ready.
Step 3: Access the Argo CD UI#
kubectl port-forward -n argocd svc/argocd-server 8080:443
In another terminal, get the initial admin password:
kubectl get secret -n argocd argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d; echo
Open https://localhost:8080 (accept the self-signed cert warning), log in as admin with that password.
You should see Argo CD's dashboard with no applications yet.
Step 4: Create a Git repo for cluster state#
We need a Git repo Argo CD can watch. Create a public repo on GitHub called gitops-demo (no README, no gitignore — keep it empty). Then locally:
mkdir gitops-demo && cd gitops-demo
git init
git branch -M main
git remote add origin https://github.com/<your-username>/gitops-demo.git
Create app.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: app
image: nginx:1.27-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: hello
namespace: default
spec:
selector:
app: hello
ports:
- port: 80
Commit and push:
git add app.yaml
git commit -m "initial nginx deployment"
git push -u origin main
Step 5: Tell Argo CD about the repo#
In the Argo CD UI:
- Click "+ NEW APP"
- Application Name:
hello - Project:
default - Sync Policy:
Automatic(check both prune and self-heal) - Repository URL: your GitHub URL (e.g.
https://github.com/yourname/gitops-demo) - Path:
.(the repo root) - Cluster: in-cluster
- Namespace:
default - Click "CREATE"
Argo CD reads the repo, sees the Deployment + Service, applies them to the cluster. Within ~30 seconds:
kubectl get pods -l app=hello
You should see two hello-... pods running. Argo CD did this — you didn't kubectl apply anything.
Step 6: Make a change via Git#
Edit app.yaml locally — change replicas: 2 to replicas: 5. Commit and push:
git commit -am "scale to 5 replicas"
git push
Within ~3 minutes (Argo's default polling interval; you can also click "REFRESH" in the UI), Argo CD picks up the change and applies it. Check:
kubectl get pods -l app=hello
You should now see five pods. The cluster reconciled to match the Git state — automatically. No kubectl apply from you.
Step 7: Watch self-heal in action#
Try to bypass GitOps:
kubectl scale deployment/hello --replicas=1
kubectl get pods -l app=hello # 1 pod (briefly)
Wait ~10 seconds and check again:
kubectl get pods -l app=hello
Five pods. Argo CD detected the drift (cluster says 1, Git says 5) and reverted. That's the self-heal mechanism — it's the GitOps promise made operational.
Step 8: Roll back#
Decide that 5 replicas was too many:
git revert HEAD --no-edit
git push
Argo CD picks up the revert, scales back to 2.
That's the complete GitOps loop: edit, commit, push, watch the cluster converge. Same flow whether you're tweaking replicas or rolling out a new image tag.
Step 9: Clean up#
kind delete cluster --name gitops-tutorial
When GitOps fits, when it doesn't#
GitOps shines when:
- You have multiple services (~5+) on Kubernetes
- Multiple people deploy to the cluster
- Audit trail and rollback matter
- "Who changed what" is a recurring question
GitOps adds friction when:
- You have one service, one deployer
- You're still iterating on architecture daily
- Your cluster is short-lived dev/CI
For most production K8s teams above a small size, the friction is worth it.
Common mistakes#
Trying to GitOps everything. Cluster bootstrap (Argo CD itself, CNI, etc.) doesn't fit cleanly — that's the chicken-and-egg problem. Use Terraform for the cluster shell, GitOps for what runs inside.
Keeping secrets in Git. Don't. Use sealed-secrets, External Secrets Operator, or Vault Agent — anything that keeps actual secret material out of the repo.
Auto-syncing everything to prod. A bug in the repo becomes a prod outage automatically. Production apps benefit from manual sync (PR auto-merges in Git, but a human clicks "Sync" to actually deploy).
Skipping the self-heal verification. "GitOps revert worked" should be tested before you trust it in an incident.
What to read next#
You've seen the model. The next levels:
- GitOps with Argo CD: automating Kubernetes deployments — what changes after running this for two years across 40+ services
- Kubernetes 101: pods, deployments, and services — the K8s fundamentals GitOps builds on
- Terraform tutorial: your first IaC project — for the cluster-bootstrap layer beneath GitOps
GitOps isn't magic. It's a disciplined version of "Git is the truth," automated by a tool that watches and reconciles. Once a team is on it, the operational mode it enables — clean audits, easy rollbacks, no console-clicks — is hard to give up.
Get the DevOps Troubleshooting Cheat Sheet
Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.
Your First CI/CD Pipeline with GitHub Actions
Walk through a working GitHub Actions workflow — install, test, build, deploy — for a tiny Node app. Every line explained.
Kubernetes 101 — Pods, Deployments, and Services Explained
Run your first three Kubernetes objects — Pod, Deployment, Service — on a local cluster, then understand why each one exists and how they fit together.
More from Infrastructure
Explore more articles in this category
Redis vs Memcached: Choosing a Cache in 2026
Both are fast in-memory stores, and both get picked by habit more than by requirements. Here is what actually differs and when each one is the right call.
Vault vs AWS Secrets Manager vs Doppler: Choosing a Secrets Tool
One is a full secrets platform, one is AWS-native and hands-off, and one is built for developer workflow. Picking by feature list alone misses the real tradeoff.
How DNS Works (Explained Simply)
A developer-friendly walk through DNS resolution, record types, TTL, and the caching quirks that cause real production bugs.
You might have missed
Evergreen posts worth revisiting.