Definition
- GitOps – an operational model where a Git repository holds the complete declared desired state as manifests, and a controller in the cluster reconciles any difference.
- Pull-based – the cluster converges itself toward Git rather than an external tool pushing changes in.
- Controller – ArgoCD or Flux runs inside the cluster, periodically fetches manifests from Git, computes a diff against live objects, and applies changes.
- Drift correction – any out-of-band change (like a manual kubectl edit) is detected and reverted automatically.
- Rollback – reverting the Git commit that changed the image tag causes the controller to roll the cluster back.
Imagine the National Bank of India's official rulebook for how every branch must be set up. In GitOps, the rulebook lives in a locked, version-controlled binder (Git), and each branch has an inspector (the controller) who constantly walks the floor comparing reality to the rulebook and fixing anything that drifted. To change a branch, you amend the master binder through a reviewed change, and the inspector makes reality match. The old push model is like sending a courier who physically rearranges the branch; if someone later moves a desk, nobody notices.
Understanding the Concepts
What is GitOps?
GitOps is an operational model where a Git repository holds the complete declared desired state of a system as Kubernetes manifests (Deployments, Services, ConfigMaps), and a controller running inside the cluster continuously reconciles the live state to match what Git declares.
# Git repo: config-repo/prod/transfer-service/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: transfer-service
namespace: prod
spec:
replicas: 3
template:
spec:
containers:
- name: transfer-service
image: registry.bank.local/transfer-service:abc1234
ports:
- containerPort: 8080
This file in Git is the single source of truth. What is in Git is what should be running in the cluster.
How does the controller work?
ArgoCD defines an Application resource pointing at a Git repo path and a target namespace. It periodically fetches the manifests, computes a diff against the live objects, and reconciles.
# ArgoCD Application resource
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: transfer-service
namespace: argocd
spec:
project: banking
source:
repoURL: https://git.bank.local/config-repo.git
targetRevision: main
path: prod/transfer-service
destination:
server: https://kubernetes.default.svc
namespace: prod
syncPolicy:
automated:
selfHeal: true # Revert out-of-band changes
prune: true # Remove resources not in Git
With automated.selfHeal: true, any manual change to the cluster is detected and reverted.
How does GitOps differ from push-based deploys?
| Aspect | GitOps (Pull-based) | Push-based (Jenkins kubectl) |
|---|---|---|
| Who deploys | In-cluster controller pulls from Git | External pipeline pushes to cluster |
| Source of truth | Git repository | Pipeline logs + cluster state |
| Drift detection | Automatic, continuous | None, manual changes persist |
| Audit trail | Every change is a reviewed Git commit | Buried in build logs |
| Rollback | git revert | Re-run old pipeline or manual command |
| Credentials | Stay inside the cluster | Pipeline holds cluster credentials |
| CI/CD separation | Clean: CI proposes, controller applies | Blurred: pipeline builds and deploys |
# Push-based: Jenkins runs kubectl from outside the cluster.
# Cluster credentials must be stored in Jenkins.
kubectl --token=$JENKINS_TOKEN set image deployment/transfer-service \
transfer-service=registry.bank.local/transfer-service:abc1234
# GitOps: CI updates the image tag in Git.
# The controller applies it from inside the cluster.
cd config-repo
sed -i 's|image:.*|image: registry.bank.local/transfer-service:def5678|' \
prod/transfer-service/deployment.yaml
git commit -am "Bump transfer-service to def5678"
git push
# ArgoCD detects the change and rolls it out.
How does drift correction work?
If someone runs a manual kubectl edit to bump replicas in Prod, the controller detects that the live state no longer matches Git and, with self-heal enabled, reverts it.
# Someone manually changes replicas to 5
kubectl scale deployment/transfer-service --replicas=5 -n prod
# ArgoCD detects the drift within the next reconcile cycle.
# It sees: Git says 3 replicas, cluster has 5.
# With selfHeal: true, it reverts to 3.
# The manual change is undone automatically.
This is impossible in a push-based model, where an out-of-band change persists unnoticed.
How does rollback work in GitOps?
Rollback is a git revert. Because the cluster always reflects Git, reverting the commit that bumped the image tag causes the controller to roll the cluster back automatically.
# The bad deploy was commit abc123.
git revert abc123
git push
# ArgoCD sees the reverted manifest and rolls the cluster back.
# No separate rollback script, no risk of the rollback itself drifting.
How does deployment work in GitOps?
The CI pipeline still builds and tests, but instead of deploying, it updates the config repo. The controller owns the actual deployment.
# GitHub Actions: CI proposes the new image tag
jobs:
build:
steps:
- run: mvn clean verify
- run: docker build -t registry.bank.local/transfer-service:$SHA .
- run: docker push registry.bank.local/transfer-service:$SHA
update-config:
needs: build
steps:
- uses: actions/checkout@v4
with: { repository: 'bank/config-repo' }
- run: |
sed -i "s|image:.*|image: registry.bank.local/transfer-service:$SHA|" \
prod/transfer-service/deployment.yaml
git commit -am "Bump transfer-service to $SHA"
git push
What is the security advantage?
In GitOps the cluster credentials never leave the cluster. The controller pulls from Git using read-only repo access. An external CI breach cannot directly touch the cluster.
Push-based: CI Server ──(cluster creds)──▶ Kubernetes API Breach of CI = direct access to cluster GitOps: CI Server ──(repo write)──▶ Git Repo Controller ◀──(repo read)── Git Repo Controller ──(local)──▶ Kubernetes API Breach of CI = can only change Git (which is reviewed)
What should a senior engineer know?
- Use the "app of apps" pattern to manage many services: one ArgoCD Application generates child Applications from a directory of service configs.
- Environment promotion is a Git commit copying a tested image tag from the QA overlay to the Prod overlay.
- CI and CD are cleanly separated: CI produces artifacts and proposes state, the controller applies it.
- Combine with Kustomize or Helm overlays to share base manifests across environments while varying image tags, replicas, and config per environment.
# Kustomize overlay for Prod
# config-repo/prod/transfer-service/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/transfer-service
images:
- name: registry.bank.local/transfer-service
newTag: abc1234
replicas:
- name: transfer-service
count: 3
Visual Overview
Push-based (Jenkins):
Developer ──▶ Git ──▶ CI Pipeline ──▶ kubectl apply ──▶ Cluster
│
Holds cluster
credentials
GitOps (ArgoCD/Flux):
Developer ──▶ Git ──▶ CI Pipeline ──▶ Git Config Repo
│
(read-only)
▼
┌─────────────────┐
│ ArgoCD/Flux │
│ (in-cluster) │
│ Compares Git │
│ vs Live State │
│ Reconciles │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Kubernetes │
│ Cluster │
└─────────────────┘
Drift correction:
Manual kubectl edit ──▶ Cluster drifts
ArgoCD detects diff ──▶ Reverts to Git state
Key Takeaway
GitOps is an operational model where a Git repository holds the complete declared desired state and an in-cluster controller continuously reconciles the cluster to match. Deploying is a reviewed Git commit that updates the image tag, not a kubectl command from an external pipeline. The controller notices the change and rolls it out.
The differences from push-based Jenkins deploys are concrete. Auditability is far stronger because what is running is exactly what the Git history and its pull-request approvals say. Drift is corrected automatically because the controller detects and reverts any manual out-of-band change. Rollback is a simple git revert that the controller applies automatically, with no rollback script and no chance of the rollback drifting.
The security advantage is a major reason banks adopt it. Cluster credentials never leave the cluster. The controller pulls from Git with read-only access, so a breach of the external CI system cannot directly touch the cluster. CI and CD are cleanly separated: CI proposes the desired state, the controller owns applying it.
Remember one sentence: GitOps makes Git the single source of truth, and the in-cluster controller keeps reality honest.
---