GitOps Basics
This page is a hands-on intro to GitOps: the repository layout, the minimal manifests, and the first Argo CD and Flux objects you need to make a cluster reconcile itself from Git.
Prerequisites
- Kubernetes 1.36.2 cluster with
kubectlaccess (containerd is the node runtime via CRI). - Git and a repository you can push to.
- Either Argo CD (latest) or Flux installed; both are shown below.
- Kustomize (bundled with
kubectl) and optionally Helm 3. - Container images already built with Docker Engine 29.6.1 (BuildKit) and pushed to a registry.
Quick install of the two agents:
# Argo CD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Flux (CLI, then bootstrap)
brew install fluxcd/tap/flux
flux installBasic Examples
1. A declarative app to deploy
GitOps needs a declarative target. Start with a plain Deployment and Service committed to Git.
# apps/web/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
containers:
- name: web
image: registry.example.com/web@sha256:abc123
ports:
- containerPort: 8080- The manifest describes end state, not steps to reach it.
- Pinning the image by digest (
@sha256:) makes the deployed version reproducible. - Three replicas is the declared truth; the agent will restore it if changed.
- This file lives in Git, never applied by hand in the GitOps model.
2. A Kustomization to render the app
Kustomize groups manifests and lets you overlay per environment without templating.
# apps/web/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yamlkubectl apply -k apps/webrenders and applies locally for testing.- Both Argo CD and Flux detect a
kustomization.yamland render it automatically. - Overlays under
overlays/prodcan patch replicas or image tags per environment.
3. Your first Argo CD Application
An Application tells Argo CD which repo path maps to which cluster namespace.
# argocd/web-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/acme/manifests.git
targetRevision: main
path: apps/web
destination:
server: https://kubernetes.default.svc
namespace: web
syncPolicy:
automated:
prune: true
selfHeal: truesourcepoints at the repo, branch, and path holding the manifests.destinationis the cluster and namespace to reconcile.automatedwithselfHealreverts manual drift;prunedeletes removed resources.
4. Your first Flux Kustomization
Flux splits "where is the source" from "what to apply" into two objects.
# flux/web.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: manifests
namespace: flux-system
spec:
interval: 1m
url: https://github.com/acme/manifests.git
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: web
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: manifests
path: ./apps/web
prune: trueGitRepositoryfetches the repo everyinterval.- The Flux
Kustomizationrenderspathand applies it with pruning. prune: trueremoves objects when they disappear from Git.
5. Making a change the GitOps way
You never edit the live cluster. You change Git and let the agent apply it.
# bump the image, commit, push - the agent does the rest
git commit -am "web: deploy sha256:def456"
git push origin main- The commit is the audit record of who changed what and when.
- Argo CD or Flux notices the new commit and reconciles automatically.
- No cluster credentials are needed on the developer's machine.
6. Checking sync status
Both tools expose whether the cluster matches Git.
argocd app get web # Synced / OutOfSync + health
flux get kustomizations # Ready, last applied revisionSyncedmeans live state equals the desired state in Git.OutOfSyncmeans a diff exists, from either a new commit or drift.- Health is separate from sync: an app can be synced but degraded.
7. Rolling back
Rollback is a Git operation, not a cluster surgery.
git revert <bad-commit>
git push origin main- Reverting the commit restores the previous declared state.
- The agent reconciles the cluster back to known-good automatically.
- No need to remember the old image tag; Git already has it.
Intermediate Examples
8. Sealing secrets for Git
Secrets cannot sit in plaintext Git, so encrypt them before committing.
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
git add sealed-secret.yamlkubesealencrypts with a public key; only the in-cluster controller can decrypt.- The
SealedSecretis safe to commit and reconcile like any other manifest. - Alternatives include SOPS-encrypted files or an external secrets operator.
9. Ordering with sync waves
CRDs must exist before the resources that use them.
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1"- Lower waves apply first; put CRDs and namespaces in negative waves.
- Flux expresses the same ordering with
dependsOnbetween Kustomizations. - Correct ordering prevents "no matches for kind" errors on first sync.
10. Auto-updating images from the registry
Image automation bumps the manifest when a new tag is pushed.
# Flux image policy: track semver in the registry
spec:
policy:
semver:
range: ">=1.0.0"- Flux writes the new tag back into Git via an
ImageUpdateAutomation. - Argo CD Image Updater performs the equivalent write-back for Argo users.
- Images themselves are built by Docker/BuildKit; GitOps only tracks which one runs.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).