GitOps is the operating model that platform engineering converged on. The idea is older than the term — declarative configuration in version control, applied by a reconciliation loop — but it is now the dominant pattern for running Kubernetes in production.
The GitOps Principles
The OpenGitOps working group (CNCF) defines four principles:
- Declarative. The desired state is described declaratively (YAML, Helm, Kustomize).
- Versioned and immutable. Desired state is stored in a way that enforces immutability and version history — Git, OCI artifacts, etc.
- Pulled automatically. Software agents pull the desired state — no
kubectl applyfrom human laptops. - Continuously reconciled. Agents observe and converge actual state to desired state.
The contrast is with "push CI/CD" — a CI job runs kubectl apply. GitOps inverts this: the cluster pulls from Git.
Why Platform Teams Love It
- Single source of truth. Everything that runs is in Git; auditability is automatic.
- Drift detection. Manual changes are surfaced and can be reverted.
- Disaster recovery. Rebuild a cluster from Git in minutes.
- Multi-cluster fan-out. The same controller drives many clusters from one repo.
- Separation of duties. Developers commit; controllers deploy. No human kubectl access required.
Argo CD vs Flux
| Argo CD | Flux | |
|---|---|---|
| CNCF status | Graduated 2022 | Graduated 2022 |
| UI | First-class web UI, sync visualisation | Minimal; relies on dashboards (Weave GitOps OSS) |
| CRDs | Application, ApplicationSet, AppProject | Kustomization, HelmRelease, GitRepository, OCIRepository |
| Multi-tenancy | AppProject + RBAC | Tenants pattern + RBAC |
| Helm | Renders charts client-side | Runs Helm directly (so hooks work) |
| Strength | UX, broad adoption, App-of-Apps | Composable, lightweight, strong CLI |
Choose one. Don't run both in the same cluster.
Argo CD Building Blocks
Application
The fundamental CRD — a pointer to a Git path (or Helm chart) plus a target cluster and namespace:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: checkout-service-prod
namespace: argocd
spec:
project: checkout
source:
repoURL: https://github.com/acme/checkout-service
targetRevision: HEAD
path: deploy/prod
destination:
server: https://prod.k8s.acme.io
namespace: checkout
syncPolicy:
automated: { prune: true, selfHeal: true }
ApplicationSet
Templates Applications across a generator — clusters, paths in a repo, list, matrix. The platform-team workhorse for fan-out:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: checkout-environments
spec:
generators:
- matrix:
generators:
- clusters: {} # all registered clusters
- list:
elements:
- { env: dev, branch: main }
- { env: staging, branch: main }
- { env: prod, branch: HEAD }
template:
metadata: { name: "checkout-{{name}}-{{env}}" }
spec:
project: checkout
source:
repoURL: https://github.com/acme/checkout-service
targetRevision: "{{branch}}"
path: "deploy/{{env}}"
destination: { server: "{{server}}", namespace: checkout }
syncPolicy: { automated: { prune: true, selfHeal: true } }
One ApplicationSet, dozens of Applications, all derived from the cluster registry and an environment list. Platform teams operate the generator; teams just commit to their repo's deploy/<env> directory.
App-of-Apps
Hierarchical composition. A single root Application points at a folder containing Application CRDs. Adding a new app is a Git commit to that folder. Useful for bootstrapping clusters (install Cert Manager, Ingress, etc.) and for multi-team aggregation.
AppProject
RBAC and constraints. Define what a project can deploy where: allowed source repos, allowed destination clusters/namespaces, allowed CRDs. Apply to all Applications labelled with that project.
Multi-Cluster Patterns
Two dominant shapes:
- Hub-and-spoke. One management cluster runs Argo CD; it deploys to many workload clusters. Central control; one place to look. Argo CD's classic pattern.
- Per-cluster Argo CD. Each cluster has its own controller pulling from Git. More resilient (a hub outage doesn't stop reconciliation) but harder to operate.
For 5-20 clusters, hub-and-spoke is simpler. For 100+ clusters or strict isolation requirements (regulated industries, edge), per-cluster wins. Argo CD's "agent" mode is bridging this in 2025 releases.
Progressive Delivery: Argo Rollouts
A plain Kubernetes Deployment does rolling updates only. Argo Rollouts adds:
- Blue-green — preview + active services, manual or automated cutover
- Canary — gradual traffic shift, weighted via service mesh or ingress controller
- Analysis — query Prometheus, Datadog, NewRelic during rollout; auto-rollback on regression
The CRD is a drop-in replacement for Deployment. Service-mesh integration (Istio, Linkerd, AWS App Mesh, NGINX) handles the traffic-splitting.
Secrets in GitOps
Plain secrets cannot live in Git. Options:
- Sealed Secrets (Bitnami) — encrypt secrets to a cluster-specific key; commit the encrypted form
- External Secrets Operator — point at Vault / AWS Secrets Manager / GCP Secret Manager; ESO syncs into native
Secretobjects - SOPS — encrypt YAML files; decrypt at apply time (Flux native support)
ESO is the dominant choice for platform teams that already operate a secret store.
The Platform Team's Contract
- You operate the GitOps controllers, the cluster registry, the project / RBAC model
- Teams own their repo and the YAML it contains
- Your golden-path service template provisions the Application/ApplicationSet entry on day one
- You publish runbooks: how to debug a sync failure, how to force a refresh, how to recover a stuck app
GitOps is not free — there is genuine operational depth — but it is the foundation modern platform teams build on. Get it right and "deploying to production" becomes "merging a PR."