Skip to main content
RunBook Academy

Git, CI/CD & GitOpsCVII · Production Infrastructure Delivery ArchitectureDeployment

The deployment flow — from plan to apply to runtime

Advanced⏱ ~26 mingitkubectlterraform

What you'll learn

  • Identify the four steps of the deployment flow as plan, approve, apply, verify
  • Distinguish a plan step from an apply step and explain why the plan must be reviewed before the apply
  • Apply the GitOps reconcile loop as a continuous deployment flow
  • Recognise the failure modes when one of the four steps is bypassed

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

The deployment flow is the path from a reviewed plan to a running resource. It has four steps, each producing a record the audit chain carries: plan, approve, apply, verify. A flow that skips a step produces a deployment that is partially attributable. A deployment without a plan is a deployment whose intent cannot be reconstructed; without approval, a deployment whose decision cannot be attributed; without verification, a deployment whose success is asserted but not measured.

The four steps

flowchart LR
    A["Manifest / Terraform code"] -->|"plan"| B["Plan artefact\n(intent)"]
    B -->|"approve"| C["Approval record\n(decision)"]
    C -->|"apply"| D["Reconcile\n(cluster matches Git)"]
    D -->|"verify"| E["Health check\n(runtime matches plan)"]
    E -->|"pass"| F["Steady state"]
    E -->|"fail"| G["Rollback"]

Each step produces a record:

  • Plan. terraform plan -out=tfplan writes a binary plan file whose digest is the intent; argocd app diff produces a structural diff whose hash is the intent.
  • Approve. PR approval or change-board sign-off records the decision identity on the receipt.
  • Apply. terraform apply reads the saved plan; Argo CD’s sync applies the manifest set and stamps the receipt.
  • Verify. Smoke tests, health checks, SLO probes confirm runtime matches plan. Continuous for GitOps; one-shot for imperative apply.

The plan step

The plan is the artefact that captures intent. Without it, the auditor cannot answer “what did this change intend to do?” six months later.

WORKDIR=infra/payments
cd "$WORKDIR"
terraform init -backend=false
terraform plan -out=tfplan -var-file=prod.tfvars
terraform show -json tfplan | jq '.resource_changes[] | {address: .address, actions: .change.actions}'

The plan file’s SHA-256 digest is the intent’s identity. The same diff computed later produces the same digest. The apply step must reference the plan file’s digest so the auditor can confirm the apply was of the reviewed plan.

For Kubernetes, the plan is the manifest diff produced by the controller’s dry-run:

APP=checkout
NS=payments
SERVER=argocd.example.com
argocd app diff "$APP" --server "$SERVER" --revision "$(git rev-parse HEAD)"

The diff is what the reviewer sees in the PR. The controller applies the same manifest set on sync.

The approve step

The approve step records the decision identity. For a PR-based flow it is the GitHub approval; for a change-managed flow it is the change-board ticket; for continuous delivery with no human gate, it is the policy engine’s decision log (OPA, Conftest, Kyverno).

The decision identity is stamped on the receipt at apply time as the decision annotation:

NS=payments
APP=checkout
kubectl annotate deployment "$APP" -n "$NS" \
  deploy.time/decision="$(gh pr view --json number,mergedBy | jq -r '.mergedBy.login')"

A deployment without a decision annotation has no attribution. The auditor walks the receipt’s revision to the commit, the commit to the PR, and the PR to the approver - but without the annotation, the walk is not on the cluster.

The apply step

The apply step reconciles the cluster to the manifest. For Terraform, it is terraform apply against the saved plan; for Kubernetes, it is the GitOps controller’s sync. The apply step is the only step that touches the cluster.

A GitOps reconcile loop runs the apply step continuously. The controller watches the manifest repo and the cluster; when they diverge, it applies the manifest set.

APP=checkout
SERVER=argocd.example.com
argocd app sync "$APP" --server "$SERVER" --prune
argocd app wait "$APP" --server "$SERVER" --health

The first applies; the second waits for the runtime to reach a healthy state. Together they are the apply-and-verify sequence.

The verify step

The verify step is continuous for a GitOps controller and one-shot for an imperative apply. The verify step is the only step that observes the outcome of the apply, not just the action.

Three signals the verify step produces:

  • Health probe. Liveness, readiness, and startup probes report whether the pod is serving.
  • Smoke test. A scripted call to the service’s public surface returns the expected response.
  • SLO check. A representative query against the golden signal returns within the SLO.

A deployment whose apply is green but whose verify fails has a running pod that is not serving. The audit chain records the apply; the verify failure requires a new change to remediate.

Production discipline

  1. The plan is the artefact, not the diff output. A saved plan file’s digest is the identity; the JSON diff is a rendering.
  2. The approve step records a decision identity. auto-merged is a decision identity; an empty decision is a missing link.
  3. Verify is continuous for GitOps, one-shot for imperative. Imperative applies need a separate verify step.

Cross-course references

  • This course, Part LXIV (Auditability) - the decision link in the audit chain.
  • This course, Part CV (GitOpsAnti) - the failure modes of unsafe apply.
  • Terraform for Production Sysadmins - Parts IX-XII cover plan/apply mechanics for infrastructure.

Quiz

Knowledge check · 4 questions

  1. Q1. A team runs `terraform apply` directly without first running `terraform plan` and saving the plan file. The apply succeeds. What is the audit-chain consequence?

  2. Q2. A GitOps controller's reconcile loop makes the verify step continuous.

  3. Q3. Name the four steps of the deployment flow in order and identify the step that captures intent.

  4. Q4. Identify which deployment-flow steps are intact and which are bypassed.

    A team uses Terraform Cloud with `auto-apply` enabled on the production workspace. The Terraform code is in a Git repo with branch protection; PR approvals are required. The auto-apply runs on every merge to main without a manual plan review. Argo CD deploys the resulting artefacts to EKS. The apply stamps the deploy.time/decision annotation with the GitHub Actions workflow identity. An incident requires reconstructing the diff that was applied.

Passing score: 75%. Answers are checked in this browser.