Scenario
Section titled “Scenario”You need to change a production setting — a replica count, a timeout, a memory limit. In a GitOps model that is not a command you run against the cluster. It is a commit, reviewed like code, applied by a reconciler.
The value of that model is not the automation. It is that the change becomes reviewable before it takes effect, and auditable afterwards.
Objective
Section titled “Objective”Take an infrastructure change through the full path: declarative manifest → pull request showing the effect → review against a checklist → promotion through environments → reconciliation.
Prerequisites
Section titled “Prerequisites”- GitOps fundamentals
- Environment promotion
- Comfortable with pull request review
Starting state
Section titled “Starting state”mkdir -p /tmp/lab-gitops && cd /tmp/lab-gitopsgit init -q .git config user.email "lab@example.com"git config user.name "Lab User"
mkdir -p environments/{staging,production} .github/workflows
cat > environments/staging/api.yaml <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata: name: apispec: replicas: 2 template: spec: containers: - name: api image: registry.example.com/api:1.4.0 resources: limits: memory: "512Mi"EOF
cp environments/staging/api.yaml environments/production/api.yamlsed -i 's/replicas: 2/replicas: 6/' environments/production/api.yaml
git add . && git commit -q -m "Add environment manifests"git log --onelineTwo environments, deliberately differing only in replica count.
-
Make the change in staging first. Raise the memory limit to
1Giinenvironments/staging/api.yamlon a branch:Terminal window git switch -qc infra/raise-api-memorysed -i 's/memory: "512Mi"/memory: "1Gi"/' environments/staging/api.yamlgit commit -qam "Raise API memory limit to 1Gi in staging" -
Produce the reviewable diff. A reviewer must see the effect, not just the text:
Terminal window git diff main -- environments/What does this diff fail to tell a reviewer? Write down two things.
-
Add a workflow that surfaces the effect. Create
.github/workflows/infra-plan.yml:name: Infrastructure planon:pull_request:paths: ['environments/**']permissions:contents: readpull-requests: writejobs:plan:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v7with:fetch-depth: 0filter: blob:none- name: Show what changedrun: |echo "### Environments touched" >> "$GITHUB_STEP_SUMMARY"git diff --name-only "origin/${{ github.base_ref }}...HEAD" \-- environments/ >> "$GITHUB_STEP_SUMMARY" -
Review it against the checklist below. Be honest about what you cannot tell from the diff.
-
Promote to production as a separate commit — never the same one:
Terminal window sed -i 's/memory: "512Mi"/memory: "1Gi"/' environments/production/api.yamlgit commit -qam "Promote API memory limit to production" -
Simulate drift and reconciliation. Change production directly, as though someone edited the cluster by hand, then observe what a reconciler would do:
Terminal window sed -i 's/replicas: 6/replicas: 12/' environments/production/api.yamlgit diffgit checkout environments/production/api.yaml # reconcile back to declared state
Review checklist
Section titled “Review checklist”Use this on the pull request from step 4:
- What resources change? Named explicitly, not implied by the diff.
- Is anything destroyed or replaced? A memory limit change may trigger a rolling replace.
- Which environment? Staging and production changing in one PR is a review smell.
- Is it reversible? State the revert path before merging, not after.
- Blast radius if the change is wrong.
- Does staging prove anything? Only if staging is representative.
- Security impact — does it touch identity, networking, permissions or secrets?
- Who is accountable — does
CODEOWNERSroute this to someone who would spot a bad plan?
Solution
Section titled “Solution”Step 2 — what the diff does not tell you:
- Whether the change is disruptive.
512Mi→1Giis one line, but on Kubernetes it triggers a rolling replacement of every pod. The diff looks trivial; the effect is not. - Whether the cluster currently matches the manifest. If someone changed things by hand, the applied result differs from what the diff implies.
Steps 5–6 — separate commits per environment. Promotion is a distinct, reviewable act. One commit changing both environments cannot be reverted for production alone, and removes the staging soak entirely.
Step 6 — drift. The manual edit and the reconcile are the whole GitOps proposition: the declared state in Git is authoritative, and anything else converges back to it. Drift that persists means either the reconciler is not running or something outside Git is fighting it.
Explanation
Section titled “Explanation”Git is not just where the manifests live — it is where the change becomes reviewable. A
kubectl edit has no diff, no reviewer, no record of why. A commit has all three.
Review needs the effect, not the text. This is why plan output belongs on the pull request.
For Terraform that is terraform plan; for Kubernetes it is a diff or dry-run. Without it, a
reviewer approves text and hopes.
Promotion is a separate commit per environment, so each is independently reviewable and independently revertible.
Reconciliation is what makes the declared state true, rather than merely intended. Without it you have manifests in Git and reality somewhere else.
Clean up
Section titled “Clean up”cd /tmp && rm -rf lab-gitopsRelated lessons
Section titled “Related lessons”Next steps
Section titled “Next steps”You have completed the labs. Choose a learning path to go deeper in whichever direction is most useful to you.