Markdown Editor

Kubernetes Cluster Upgrade Runbook Template (Minor Version)

Minor-version upgrade runbook: support and skew check, deprecated API scan, add-on and CRD compatibility, PDB readiness, backups, control plane then nodes, verification, rollback limits

ОпераціїПовнийKubernetes release and version skew policy (1.37)CC BY 4.0kubernetesclusterupgraderunbook

Як використовувати: Use it for every Kubernetes minor-version step on a cluster, one runbook per step. Fill the support and version skew check and the deprecated API scan first, and rehearse in staging. The main pitfall is assuming the control plane can be downgraded; treat that step as the point of no return.

Перегляд

Kubernetes Cluster Upgrade Runbook: [Cluster name] [source version] to [target version]

Runbook for one minor-version upgrade of a Kubernetes cluster: support window and version skew check, deprecated API scan, add-on and CRD compatibility, etcd backup, control plane first and nodes second, surge and drain with PodDisruptionBudget checks, verification, rollback limits and communication. It includes a worked example of an upgrade from 1.36 to 1.37. Based on the Kubernetes release and version skew policy documentation (kubernetes.io, CC BY 4.0), checked on 2026-10-24 when 1.37 was the newest minor release. Replace every [bracketed] value and every row marked Example.

Document control

FieldValue
Document ID[KUR-001]
Cluster[prod-eu-1]
Environment[Production / Staging / Development]
Source version (control plane and nodes)[1.36.5]
Target version[1.37.1]
Hosting model[Managed service / kubeadm / Distribution]
Change request[CHG-0000]
Planned window[YYYY-MM-DD HH:MM to HH:MM time zone]
Upgrade lead[@upgrade-lead]
Version[0.1]
Status[Draft / Approved / In progress / Completed / Rolled back]
Owner[@platform-lead]
Approvers/reviewers[@sre-lead], [@service-owner], [@change-manager]
Last updatedYYYY-MM-DD
Classification[Internal]

Approvals

RoleNameDecisionDate
Platform lead[@name][Approve / Approve with conditions / Reject]YYYY-MM-DD
SRE lead[@name][Decision]YYYY-MM-DD
Change manager[@name][Decision]YYYY-MM-DD

Revision history

VersionDateAuthorChange summary
0.1YYYY-MM-DD[@name]First draft
1.0YYYY-MM-DD[@name]Example: Approved after staging rehearsal
1.1YYYY-MM-DD[@name]Example: Executed in production, outcome recorded in section 15

Contents

1. Scope and summary

State the scope in a short paragraph: which cluster, which control plane and node pools, which add-ons move with the upgrade and which do not. Name the trigger (cadence, approaching end of life, needed feature, security fix) and the business deadline.

ItemUpgraded in this changeFromTo
Control plane (Example)Yes1.36.51.37.1
Node pool np-system (Example)Yes1.36.51.37.1
Node pool np-general (Example)Yes1.36.51.37.1
Node pool np-memory (Example)Yes1.36.51.37.1
Node pool np-gpu (Example)Deferred, within skew1.36.51.36.5
CNI plugin (Example)Yes, required by its support matrix[x.y][x.z]
Cluster DNS, metrics server (Example)Yes, provider managedProviderProvider

Rules for this runbook:

  1. Upgrade one minor version per change. Never skip a minor version.
  2. Upgrade staging first and let it run for at least [5] business days before production.
  3. Upgrade the control plane first, then node pools. Kubelets must never be newer than the API server.
  4. Freeze application deployments to the cluster for the window, except emergency fixes approved by the upgrade lead.
  5. Stop and roll back at any failed verification gate; do not improvise in production.

2. Roles and contacts

RoleResponsibilityNameContact
Upgrade leadRuns the procedure, decides go, pause or rollback[@name][Channel or phone]
Second engineerVerifies each step, keeps the execution log[@name][Contact]
Service owners on standbyCheck their services at verification gates[@names][Contact]
Incident commander on callTakes command if the upgrade causes an incident[@name][Paging route]
CommunicationsStatus page and internal updates[@name][Contact]
Provider supportEscalation for managed control plane issues[Plan and case link][Contact]

If the upgrade causes customer impact, declare an incident and hand command to the incident commander; roles then follow Incident Commander Handbook (ops/incident-commander-handbook) and severity follows Incident Severity Matrix (ops/incident-severity-matrix).

3. Support window and version skew check

3.1 Support window

Check the release table at kubernetes.io/releases and the provider's version calendar on the day of planning and again on the day before the window.

CheckValueSourceResult
Target minor version supported upstream[1.37, end of life YYYY-MM-DD]kubernetes.io/releases[Pass / Fail]
Target patch is the latest patch of the minor[1.37.1]kubernetes.io/releases[Pass / Fail]
Target available and supported by provider[Yes, standard support until YYYY-MM-DD]Provider calendar[Pass / Fail]
Source version end of life[YYYY-MM-DD]kubernetes.io/releases[Information]

3.2 Version skew

The Kubernetes version skew policy, as published on 2026-10-24, sets these limits relative to the API server:

ComponentAllowed relative to kube-apiserver
Other kube-apiserver instances (HA)Within one minor version of each other
kubeletNot newer; up to three minor versions older
kube-proxyNot newer; up to three minor versions older, and within three minor versions of the kubelet on the same node
kube-controller-manager, kube-scheduler, cloud-controller-managerNot newer; expected to match, may be one minor version older
kubectlWithin one minor version, older or newer

While API servers in a highly available control plane run mixed versions, the allowed range for the other components narrows to the oldest API server. Some providers and tools impose tighter limits than the upstream policy; record them.

ComponentCurrent versionAllowed after control plane reaches targetAction
kubelet, pool np-general (Example)1.36.51.37, 1.36, 1.35, 1.34Upgrade in section 10
kubelet, pool np-memory (Example)1.36.5SameUpgrade in section 10
kubelet, pool np-gpu (Example)1.36.5SameDefer to next window, still within skew
kubectl in CI images (Example)1.351.38, 1.37, 1.36Update CI image to 1.37 before the window

Any kubelet or kube-proxy already three minor versions behind the API server must be upgraded before the control plane can move.

4. Release notes and deprecated API scan

4.1 Release notes review

Read the release notes and the "urgent upgrade notes" of the target minor version and of every patch in between, plus the Kubernetes deprecated API migration guide. Record each item that affects this cluster.

IDChange in target releaseAffectsActionOwnerStatus
KUR-RN-001 (Example)[Removed API version or changed default][Charts, operators, manifests][Migrate to new version][@name][Done / Open]
KUR-RN-002 (Example)[Feature gate graduated or removed][Component flags][Remove flag][@name][Status]
KUR-RN-003 (Example)[Changed default in kubelet][Node pools][Test in staging][@name][Status]

4.2 Deprecated API scan

Use at least two sources, because each sees a different part of the picture:

  1. Live cluster usage. Query the API server metric apiserver_requested_deprecated_apis and audit events annotated as deprecated over a period that covers monthly jobs (at least [30] days of audit data). These show which clients still call deprecated versions.
  2. Stored objects and manifests. Scan Git repositories, Helm releases and rendered manifests with a deprecated API detection tool, and render every chart with the target version's API list.
  3. Admission webhooks. Check that webhooks match new resource versions; confirm that no webhook sets matchPolicy: Exact, so that requests for equivalent API versions are still intercepted (Equivalent is the default in admissionregistration.k8s.io/v1).
# Example: list deprecated API calls seen by the API server
kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis
Finding IDObject or clientDeprecated versionReplacementFix ownerFixed in staging
KUR-API-001 (Example)[Chart or controller name][group/version][group/version][@team][Yes / No]

Exit criterion: no client of a version removed in the target release in the last [30] days of audit data, and no stored manifest using one.

5. Add-on, CRD and operator compatibility

ComponentInstalled versionSupports target?Required versionUpgrade orderSource
CNI plugin (Example)[x.y]No[x.z]Before control planeVendor compatibility matrix
Gateway API implementation (Example)[x.y]YesSameNo changeVendor notes
Policy engine or admission webhooks (Example)[x.y]Yes from [x.z][x.z]Before control planeRelease notes
Certificate controller (Example)[x.y]YesSameNo changeRelease notes
Storage CSI drivers (Example)[x.y]YesSameAfter control planeProvider
Monitoring stack (Example)[x.y]YesSameNo changeRelease notes
Backup tool (Example)[x.y]Yes from [x.z][x.z]Before control planeRelease notes
Autoscaler (Example)Version tied to minorNoVersion matching 1.37With control planeProject docs

For each CRD-backed operator, record the stored versions (status.storedVersions of the CRD) and confirm that the new operator release still serves them or migrates them. Upgrade operators in staging first and run their own upgrade notes.

6. Workload disruption readiness

Draining a node evicts its pods through the eviction API, which honours PodDisruptionBudgets. Check before the window:

  1. Every production workload with two or more replicas has a PodDisruptionBudget.
  2. No PodDisruptionBudget has ALLOWED DISRUPTIONS at 0 in steady state, unless the owner has agreed a manual step.
  3. Single-replica workloads are listed and their owners accept a short outage, or scale to two replicas for the window.
  4. Stateful workloads with quorum (databases, queues, search) have a PodDisruptionBudget that keeps quorum and an owner on standby.
  5. Workloads use readiness probes so that traffic shifts only to ready pods.
  6. Pods with local storage (emptyDir) are known; their data is lost on drain.
# Example: find budgets that currently allow no disruption
kubectl get pdb -A -o wide | awk 'NR==1 || $5 == 0'
WorkloadNamespaceReplicasPDBAllowed disruptionsAction
search-replica (Example)catalog3minAvailable 30Change to maxUnavailable 1 before window
legacy-report (Example)finance1NoneNot applicableOwner accepts 2 minute restart

7. Backup and rollback limits

Take and verify these backups no more than [2] hours before the window:

BackupMethodLocationVerified by
etcd snapshot (self-managed)etcdctl snapshot save, then etcdutl snapshot statusEncrypted bucket outside cluster[@name]
Namespaced objects and volumesBackup tool run for all namespacesBackup vault[@name]
Cluster configurationGit commit hash of the GitOps repositoryGit[@name]
Provider settingsExport of cluster and node pool configurationChange record attachment[@name]

Rollback limits to accept before approval:

  • Control plane. Downgrading the control plane to the previous minor version is not a supported path upstream, and managed services generally do not offer it. Once the API server has written objects in the new version, the only full rollback is restoring etcd from the snapshot (self-managed) or rebuilding the cluster from Git and backups. Treat the control plane step as the point of no return.
  • Nodes. Node pools can be rolled back by creating a new pool at the previous version (allowed, because kubelets may be older than the API server) and moving workloads to it.
  • Add-ons. Roll back add-ons through their own release history, provided their CRD versions allow it.
  • Data written during the window. Restoring etcd discards cluster changes after the snapshot; workloads' external data is not affected.

8. Go or no-go checklist

IDCheckEvidenceResult
KUR-GO-01Staging upgraded to the same target and stable for [5] business daysStaging runbook record[Pass / Fail]
KUR-GO-02Version support and skew checks pass (section 3)Section 3 tables[Pass / Fail]
KUR-GO-03No removed API in use (section 4)Scan output[Pass / Fail]
KUR-GO-04Add-ons at compatible versions (section 5)Section 5 table[Pass / Fail]
KUR-GO-05PodDisruptionBudgets allow disruption (section 6)Command output[Pass / Fail]
KUR-GO-06Backups taken and verified (section 7)Backup job IDs[Pass / Fail]
KUR-GO-07Spare quota for surge nodes in each zoneQuota report[Pass / Fail]
KUR-GO-08No open SEV1 or SEV2 incident on the cluster (Incident Severity Matrix, ops/incident-severity-matrix)Incident tool[Pass / Fail]
KUR-GO-09Change approved and maintenance notice sentChange record[Pass / Fail]
KUR-GO-10Error budget of affected SLOs not exhaustedSLO dashboard[Pass / Fail]

Decision: [Go / No-go], recorded by [@upgrade-lead] at [YYYY-MM-DD HH:MM].

9. Procedure: control plane

Record the start and end time of each step in the execution log.

  1. Upgrade add-ons that must move first (section 5), one at a time, verifying each. Verify: add-on pods ready, no new errors in their logs.
  2. Upgrade the API server.
    • Managed service: start the control plane upgrade to the target patch through the provider interface or infrastructure code. Verify: the provider reports the target version and the cluster as healthy.

    • Self-managed with kubeadm: follow the kubeadm upgrade page on kubernetes.io for the target minor, in this order.

      1. On every node, switch the package repository to the target minor.
      2. On the first control plane node: upgrade the kubeadm package to the target patch, check it with kubeadm version, run sudo kubeadm upgrade plan, then sudo kubeadm upgrade apply v[target]. Upgrade the CNI plugin if its provider requires it (section 5).
      3. On each other control plane node, one at a time: upgrade the kubeadm package, then run sudo kubeadm upgrade node.
      4. Then, on each control plane node in turn: kubectl drain <node> --ignore-daemonsets, upgrade the kubelet and kubectl packages to the target patch, run sudo systemctl daemon-reload and sudo systemctl restart kubelet, then kubectl uncordon <node>.
      5. Self-managed worker nodes that are upgraded in place instead of replaced follow the same pattern in section 10: upgrade kubeadm, run sudo kubeadm upgrade node, drain, upgrade kubelet and kubectl, restart kubelet, uncordon.

      Verify: each API server reports the target version, and kubectl get nodes shows every control plane node Ready at the target kubelet version.

  3. Controller manager, scheduler and cloud controller manager. These move with the control plane on managed services and kubeadm. Verify: their leases renew and no crash loops occur.
  4. Check API health.
kubectl version
kubectl get --raw='/readyz?verbose'
kubectl get nodes -o wide
kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded
  1. Gate 1. Service owners confirm their dashboards show no regression for [15] minutes. If any check fails, stop and go to section 12.

10. Procedure: node pools

Upgrade pools in this order: system pool, then general pools, then special pools (GPU, memory, spot). For each pool:

  1. Set surge settings. Example: max surge 1 node per zone, max unavailable 0, so capacity never drops.
  2. Start the pool upgrade (managed) or create a replacement pool at the target version (self-managed or blue-green). Kubelets must not be upgraded across minor versions while pods run on the node: drain the node first. For kubeadm nodes upgraded in place, work one node (or one zone batch) at a time: upgrade the kubeadm package, run sudo kubeadm upgrade node, drain (step 3), upgrade the kubelet and kubectl packages to the target patch, run sudo systemctl daemon-reload and sudo systemctl restart kubelet, then kubectl uncordon <node>.
  3. Drain each old node.
kubectl cordon <node>
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data --timeout=15m
  1. Watch evictions. If a drain waits on a PodDisruptionBudget for more than [15] minutes, page the workload owner; do not force eviction of quorum workloads.
  2. Verify each zone batch: new nodes Ready at the target version, pods rescheduled and ready, no pending pods.
  3. Delete old nodes or the old pool once it is empty.
  4. Gate 2 per pool: service owners confirm no regression for [15] minutes.
PoolNodesSurgeStartEndIssues
np-system (Example)31 per zoneHH:MMHH:MMNone
np-general (Example)241 per zoneHH:MMHH:MMPDB wait on search-replica, 6 min
np-memory (Example)61 per zoneHH:MMHH:MMNone

11. Verification

IDCheckMethodExpectedResult
KUR-VER-01Control plane and all nodes at targetkubectl get nodesAll target, except deferred pools[Pass / Fail]
KUR-VER-02System add-ons healthyPods in system namespacesAll ready, no restarts[Pass / Fail]
KUR-VER-03No pending or crash-looping podsPod status queryNone new since baseline[Pass / Fail]
KUR-VER-04DNS resolution in and out of clusterTest podSuccess[Pass / Fail]
KUR-VER-05Ingress and Gateway routesSynthetic checksAll green[Pass / Fail]
KUR-VER-06AutoscalingScale test deploymentNew node joins at target[Pass / Fail]
KUR-VER-07StorageCreate and attach a test volumeBound and mounted[Pass / Fail]
KUR-VER-08Admission policies enforceApply a non-compliant test podRejected[Pass / Fail]
KUR-VER-09SLOsSLO dashboards for [60] minutesNo burn alert[Pass / Fail]
KUR-VER-10Backup job after upgradeRun backupSucceeds[Pass / Fail]
KUR-VER-11Benchmark scanHardening scanNo new deviations[Pass / Fail]

12. Rollback and recovery

StageTriggerActionDecision by
Before control plane stepAny go or no-go item failsCancel; rescheduleUpgrade lead
Add-on upgrade failsAdd-on not ready after [10] minutesRoll back add-on releaseUpgrade lead
Control plane upgrade fails (managed)Provider reports failureOpen provider case at highest priority; keep nodes at source versionUpgrade lead with provider
Control plane upgraded, services degradedGate 1 failsDeclare incident; fix forward where cause is known; otherwise restore etcd (self-managed) or fail over to the other regionIncident commander
Node pool upgrade degrades servicesGate 2 failsPause pool upgrade; recreate a pool at the source version (within skew) and move workloadsUpgrade lead
Data or object lossMissing objects foundRestore namespaces from backupIncident commander

After any rollback, update the change record, keep the cluster in its current state until a review decides the next step, and record the cause in a postmortem using Postmortem (dev/postmortem).

13. Communication plan

WhenAudienceChannelMessageOwner
T minus 10 business daysProduct teamsEngineering channel and emailWindow, freeze, actions on deprecated APIs and PDBsUpgrade lead
T minus 5 business daysCustomers (if visible impact possible)Maintenance noticeWindow and expected impactCommunications
T minus 1 dayApproversChange meetingGo or no-go resultUpgrade lead
Start of windowEngineering, supportIncident channel and status page if publicUpgrade startedCommunications
Each gateEngineeringUpgrade channelGate resultSecond engineer
End of windowAll aboveSame channelsCompleted or rolled back, next stepsUpgrade lead

14. Worked example: 1.36 to 1.37

Example: Corvid Commerce upgrades the prod-eu-1 cluster (managed control plane, four node pools) from 1.36.5 to 1.37.1. Kubernetes 1.37.0 was released upstream on 2026-08-26; on 2026-10-24 the upstream table lists 1.37, 1.36, 1.35 and 1.34 as maintained, with 1.34 reaching end of life on 2026-10-27.

StepDay and time (Example)DetailResult
PlanningDay minus 15Provider offers 1.37.1; staging upgraded day minus 12Staging stable 7 business days
API scanDay minus 10Audit data for 30 days: one batch job used an API version deprecated (not removed) in 1.37 through an old client library; no client used a removed versionLibrary updated; logged as KUR-API-001 for the next minor
Add-on checkDay minus 8CNI requires a newer minor for 1.37; backup tool supportedCNI upgraded in staging, then production on day minus 3
PDB checkDay minus 2search-replica budget allowed 0 disruptionsChanged to maxUnavailable 1
Go or no-goDay minus 1, 16:0010 of 10 checks passGo
BackupsDay 0, 21:10Namespace and volume backup, Git hash recordedVerified
Control planeDay 0, 21:30 to 21:52Provider upgrade to 1.37.1Gate 1 passed at 22:07
np-systemDay 0, 22:10 to 22:313 nodes, surge 1 per zoneGate 2 passed
np-generalDay 0, 22:35 to 23:5824 nodes in batches of 3 (one per zone): 8 batches at about 10 minutes eachOne PDB wait of 6 minutes
np-memoryDay 1, 00:02 to 00:406 nodesPassed
np-gpuDeferredDriver not yet validated; 1.36 kubelet within skewNew runbook KUR-002 within 30 days
VerificationDay 1, 00:45 to 01:45KUR-VER-01 to 11All pass

Totals for the example: 33 nodes replaced (3 + 24 + 6), window used 4 hours 35 minutes of the 6 hours booked, no customer impact.

15. Outcome and closure

FieldValue
Outcome[Completed / Completed with deferrals / Rolled back]
Final versions[Control plane x.y.z; pools and versions]
Deviations from runbook[List or "None"]
Incidents raised[Incident IDs or "None"]
Deferred items[Pool or add-on, owner, due date]
Lessons learned[Two or three points to change in the next runbook]
Change record closed[CHG-0000, date]
Next minor upgrade planned[Target version, environment, month]

16. Glossary and references

16.1 Glossary

TermMeaning
Minor versionThe second number in a Kubernetes version (1.37); upgrades move one at a time
Patch releaseBug and security fix release within a minor version (1.37.1)
Version skewAllowed version difference between components and the API server
Deprecated APIAPI version still served but scheduled for removal in a later release
DrainCordoning a node and evicting its pods through the eviction API
Surge upgradeAdding new nodes before removing old ones so capacity does not drop
PodDisruptionBudgetLimit on how many pods of a set may be voluntarily disrupted at once
Stored versionCRD version in which objects are persisted in etcd

16.2 References

ReferenceVersion or date checkedUse
Kubernetes Releases, kubernetes.io/releasesChecked 2026-10-24Supported versions and end of life dates
Kubernetes 1.37 release page, kubernetes.io/releases/1.371.37.0 released 2026-08-26Worked example
Kubernetes Version Skew Policy, kubernetes.io/releases/version-skew-policyChecked 2026-10-24Skew table and upgrade order
Kubernetes Deprecated API Migration Guide, kubernetes.io/docs/reference/using-api/deprecation-guideCheck for the target releaseRemoved APIs
Kubernetes documentation: Upgrading kubeadm clusters, Safely Drain a Node, Specifying a Disruption Budget, Operating etcd clustersChecked 2026-10-24Procedure concepts in original words
Provider upgrade documentation[Provider, date]Managed control plane steps

Standard and licence note

Based on the Kubernetes release, version skew policy and task documentation published at kubernetes.io by The Kubernetes Authors under the Creative Commons Attribution 4.0 International licence (CC BY 4.0, https://creativecommons.org/licenses/by/4.0/). The skew limits are summarised in original words and no documentation text is copied. Kubernetes is a registered trademark of The Linux Foundation. Other product and service names are trademarks of their owners; this template is not affiliated with or endorsed by them. This template is original text written for this collection. Check the current release table, skew policy and release notes for the target version before use. Template licence: MIT.

Схожі шаблони