Skip to content

Lab: Ship an Infrastructure Change Through a Pull Request

Lesson 6 of 2Advanced3 min readHands-On Git & GitHub Labs · DevOps Labs
Time30 minutes
LevelAdvanced
You needLocal Git; a GitHub repository if you want to run the workflow

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.

Take an infrastructure change through the full path: declarative manifest → pull request showing the effect → review against a checklist → promotion through environments → reconciliation.

Terminal window
mkdir -p /tmp/lab-gitops && cd /tmp/lab-gitops
git 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/v1
kind: Deployment
metadata:
name: api
spec:
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.yaml
sed -i 's/replicas: 2/replicas: 6/' environments/production/api.yaml
git add . && git commit -q -m "Add environment manifests"
git log --oneline

Two environments, deliberately differing only in replica count.

  1. Make the change in staging first. Raise the memory limit to 1Gi in environments/staging/api.yaml on a branch:

    Terminal window
    git switch -qc infra/raise-api-memory
    sed -i 's/memory: "512Mi"/memory: "1Gi"/' environments/staging/api.yaml
    git commit -qam "Raise API memory limit to 1Gi in staging"
  2. 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.

  3. Add a workflow that surfaces the effect. Create .github/workflows/infra-plan.yml:

    name: Infrastructure plan
    on:
    pull_request:
    paths: ['environments/**']
    permissions:
    contents: read
    pull-requests: write
    jobs:
    plan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
    with:
    fetch-depth: 0
    filter: blob:none
    - name: Show what changed
    run: |
    echo "### Environments touched" >> "$GITHUB_STEP_SUMMARY"
    git diff --name-only "origin/${{ github.base_ref }}...HEAD" \
    -- environments/ >> "$GITHUB_STEP_SUMMARY"
  4. Review it against the checklist below. Be honest about what you cannot tell from the diff.

  5. Promote to production as a separate commit — never the same one:

    Terminal window
    sed -i 's/memory: "512Mi"/memory: "1Gi"/' environments/production/api.yaml
    git commit -qam "Promote API memory limit to production"
  6. 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.yaml
    git diff
    git checkout environments/production/api.yaml # reconcile back to declared state

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 CODEOWNERS route this to someone who would spot a bad plan?

Step 2 — what the diff does not tell you:

  1. Whether the change is disruptive. 512Mi1Gi is one line, but on Kubernetes it triggers a rolling replacement of every pod. The diff looks trivial; the effect is not.
  2. 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.

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.

Terminal window
cd /tmp && rm -rf lab-gitops

You have completed the labs. Choose a learning path to go deeper in whichever direction is most useful to you.

Choose a learning pathA sequenced route through the curriculum for wherever you are now.