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.
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.
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:
git revert rolls back the change; the agent picks it up and reverts the cluster.That's GitOps. The rest is implementation.
A few problems GitOps solves:
kubectl edit during an incident to fix something. Next CI deploy reverts their change — sometimes during the next incident.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).
Both watch a Git repo and reconcile a Kubernetes cluster:
We'll use Argo CD. The model is identical for Flux.
kind create cluster --name gitops-tutorial
kubectl get nodes # should show one Ready node
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.
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.
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
In the Argo CD UI:
hellodefaultAutomatic (check both prune and self-heal)https://github.com/yourname/gitops-demo). (the repo root)defaultArgo 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.
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.
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.
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.
kind delete cluster --name gitops-tutorial
GitOps shines when:
GitOps adds friction when:
For most production K8s teams above a small size, the friction is worth it.
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.
You've seen the model. The next levels:
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 latest tutorials, guides, and insights on AI, DevOps, Cloud, and Infrastructure delivered directly to your inbox.
Walk through a working GitHub Actions workflow — install, test, build, deploy — for a tiny Node app. Every line 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.
Explore more articles in this category
Provision real cloud resources with Terraform — a VPC, an S3 bucket, and an EC2 instance — using the standard init/plan/apply workflow.
Install Ansible, write your first playbook, and configure a remote server (nginx + a deploy user) without touching it manually. The basics that scale up.
We launched Backstage in October. Six months in, 80% of services are catalogued, on-boarding takes a third of the time, and we mostly know what owns what.