Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLIX · RollbackKubernetes

Kubernetes rollback via Deployment history — kubectl rollout undo and the revision model

Advanced⏱ ~22 mingit

What you'll learn

  • Inspect a Deployment revision history with kubectl rollout history
  • Roll back to the previous revision with kubectl rollout undo
  • Roll back to a specific revision with kubectl rollout undo --to-revision
  • Recognise the boundaries the Deployment rollback does not cross

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 is the unit of rollback in Kubernetes. The Deployment controller creates a new ReplicaSet for every change to the pod template, scales it up, scales the old one down, and remembers both. The history is the rollback: every retained previous ReplicaSet is one undo away from being current again. The mechanism is internal to the controller; the operator’s interface is kubectl rollout undo.

How the revision model works

A Deployment stores its current pod template directly in .spec.template. The controller keeps one ReplicaSet per template revision, each linked to the Deployment by ownerReferences and stamped with a revision number in the deployment.kubernetes.io/revision annotation. Every change to the pod template creates a new ReplicaSet; a rollback to a previous revision copies the matching ReplicaSet’s template back into .spec.template, and the controller scales that ReplicaSet up and the others down.

flowchart LR
    A["Deployment"] --> B["ReplicaSet rs1 (rev 1)"]
    A --> C["ReplicaSet rs2 (rev 2)"]
    A --> D["ReplicaSet rs3 (rev 3)"]
    B -->|"scaled down"| X["0 replicas"]
    C -->|"scaled down"| X
    D -->|"scaled up"| Y["N replicas"]

The controller keeps this history within a window. Each old ReplicaSet remains in the cluster, scaled to zero, until it falls outside .spec.revisionHistoryLimit; the revision list is the audit trail of the pod templates the Deployment has applied within that window. The limit counts old ReplicaSets, excluding the current one; the default is 10.

Inspecting the history:

kubectl rollout history deployment/$NAME

Output is a table of revisions, each annotated with the change-cause when the rollout set the kubernetes.io/change-cause annotation (the old --record flag that once did this was removed in kubectl 1.33). The change-cause is the human-readable label that ties a revision to a commit and a rationale.

Rolling back

Three commands cover the rollback cases:

kubectl rollout undo deployment/$NAME
kubectl rollout undo deployment/$NAME --to-revision=$N
kubectl rollout status deployment/$NAME

The first command reverts to the previous revision. The second reverts to a specific revision number from rollout history. The third blocks until the rollback completes (or fails) and prints the rollout state.

sequenceDiagram
    participant Op as Operator
    participant K as kubectl
    participant C as Controller
    participant RS as ReplicaSets
    Op->>K: rollout undo deployment/$NAME --to-revision=$N
    K->>C: patch Deployment
    C->>RS: scale up rev $N
    C->>RS: scale down others
    RS-->>C: pods ready
    C-->>Op: rollout complete

A GitOps-native rollback is driven by a Git change: revert the bad commit and let the controller sync the previous manifests. Argo CD also offers an emergency shortcut that re-applies a previously deployed version from its own deployment history:

argocd app history $APP_NAME
argocd app rollback $APP_NAME $HISTORY_ID

The shortcut makes no Git change: it syncs the previously rendered manifests from Argo CD’s history, so Git still points at the bad revision. It refuses to run while automated sync is enabled - disable it first with argocd app set $APP_NAME --sync-policy none - and it must be reconciled afterwards with a git revert before auto-sync is re-enabled. Flux has no rollback command at all; the Flux path is git revert followed by flux reconcile kustomization $NAME --with-source.

What the controller does not undo

The Deployment controller owns the workload’s pod template. It does not own the surrounding cluster:

  • A ConfigMap or Secret that the Deployment no longer references is not rolled back. The previous ReplicaSet may reference a ConfigMap that the new manifest deleted; the rollback succeeds at the pod level but the pods cannot start.
  • A PersistentVolume whose claim changed. A rollback that re-attaches an old PVC may collide with the workload’s new storage assumption.
  • A Service or Ingress whose selector changed. A rolled-back Deployment may have labels that the Service no longer selects; the rolled-back pods receive no traffic.
  • A Node label or taint that the new pod template required. The previous ReplicaSet may not tolerate a taint the new ReplicaSet was designed for; the rollback succeeds but the pods stay Pending.
  • A CRD or operator-managed resource. The controller does not manage CRDs; a CRD that was removed cannot be restored by kubectl rollout undo.

The rollback’s reach is the workload, not the cluster. A team that rolls back a Deployment that depends on a missing CRD has rolled back one boundary while leaving another drifted.

When to use —to-revision

The default kubectl rollout undo reverts to the previous revision. The --to-revision=$N flag reverts to a specific revision from the history list.

Use cases for explicit revisions:

  • Rolling back past an intermediate commit. A team that rolled forward through v1, v2, v3, v4 and wants to revert to v2 (skipping v3 and v4) uses --to-revision=2.
  • Restoring a known-good revision across environments. Staging has been promoted to v5; production is still on v4. An incident in production requires reverting to the revision that matches v3. --to-revision=3 does this precisely.
  • Auditability. A specific revision number can be named in a post-mortem and reproduced.
kubectl rollout undo deployment/$NAME --to-revision=$N

The flag is also a guard against accidental rollback target: the operator must look up the revision, confirm the change-cause matches the expected good state, and pass the number explicitly.

Production discipline

  1. Configure .spec.revisionHistoryLimit to a value that covers the rollback window the team commits to (commonly 10 or higher). The limit counts old ReplicaSets, excluding the current one: 0 deletes every old ReplicaSet and disables rollback; 1 retains one old ReplicaSet, so only the immediately previous revision is still reachable.
  2. Record the change-cause on every rollout via the kubernetes.io/change-cause annotation. The CI pipeline that applies the change is the right place.
  3. Watch the rollback, do not fire and forget. The kubectl rollout status command blocks until the rollback completes; a stuck rollback means the new pods are not becoming ready.
  4. Verify the surrounding cluster after a rollback: ConfigMaps, Secrets, PVCs, Services, CRDs. The controller rolled back the workload; the cluster resources it depends on are the operator’s responsibility.

Cross-course references

  • This course, Part LVIII-02 (Rolling update) covers the ReplicaSet model that the rollback restores.
  • Kubernetes for Production Sysadmins - Part XXII (Updates) covers Deployment rollback and the change-cause annotation.
  • This course, Part XLVIII (Conditions) covers the pipeline triggers that drive rollouts and rollbacks.

Quiz

Knowledge check · 4 questions

  1. Q1. A team runs `kubectl rollout undo deployment/api` after a failed release. The rollback reports 'rollout complete' but the application's pods stay Pending. What is the most likely boundary the rollback did not cross?

  2. Q2. Setting `.spec.revisionHistoryLimit: 0` on a Deployment is a valid optimisation that reduces etcd storage by keeping only the current ReplicaSet, with no operational consequences.

  3. Q3. Name the three kubectl commands that cover the Deployment rollback cases, and state what each does.

  4. Q4. Diagnose why a Deployment rollback appeared to succeed but the application remained unhealthy, and identify the cluster resources that must be checked.

    A team runs a stateless API with revisionHistoryLimit: 10 and the kubernetes.io/change-cause annotation set by the CI pipeline. A release ships a new ConfigMap that the application reads, and the Deployment's pod template is updated to reference it. An hour after the release, an incident requires rolling back. The team runs `kubectl rollout undo deployment/api`; the rollback completes successfully; the previous ReplicaSet is scaled up. But the application reports 'config not found' in its logs. Investigation finds that the ConfigMap the previous pod template referenced was deleted by the new release's manifest apply.

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