Cluster Upgrades in Kubernetes
Kubernetes ships a new version about every four months, and every cluster has to keep up. Learn how upgrades really work, in what order things must change, how to keep your applications online while it happens, and how to recover when something goes wrong.
Imagine you live in a large apartment building, and the owner announces that the plumbing, the wiring and the elevators all need replacing this year. Nobody wants to move out. Everyone still needs water in the morning and light at night. So the workers plan carefully: one floor at a time, one pipe at a time, with notices on every door and a spare route for the water while the old one is switched off.
Upgrading a Kubernetes cluster is exactly like that. Your cluster is the building. Your applications are the residents who refuse to leave. The Kubernetes software that runs the cluster must be replaced piece by piece, and during all that work the lights must stay on. Done with care, upgrades are boring, and boring is the goal. Done carelessly, they cause some of the most memorable outages in our industry.
This guide assumes you know nothing about the topic. We will explain every new word the first time it appears, use everyday comparisons for each big idea, and build up from the basic rules of version numbers to the habits that experienced platform teams use to upgrade dozens of clusters without losing sleep.
Why Upgrades Matter, and Why They Feel Scary
Before learning the steps, it helps to understand why you cannot simply leave a working cluster alone.
What is a cluster upgrade?
A Kubernetes cluster is a group of computers that work together to run your applications. Some of these computers form the control plane, which is the brain that decides what should run where. The others are worker nodes, the machines that actually run your containers. A cluster upgrade means moving all of these parts from one Kubernetes version to a newer one, for example from version 1.30 to version 1.31.
It sounds like installing an update on a phone, but there is a big difference. A phone is one device and it can restart whenever it likes. A cluster is dozens or hundreds of machines running thousands of other people’s programs, and every one of those programs expects the cluster to keep working while it is being changed.
Think of an airline that wants to fit new engines to its planes. It cannot cancel every flight for a month. Instead it takes one plane out of service at a time, swaps the engines, tests it, and puts it back, while the other planes carry on flying the schedule. A cluster upgrade is the same dance: take a little capacity out, change it, test it, return it, and move on to the next piece.
Four reasons you must keep upgrading
Old versions stop getting fixes
The Kubernetes community only provides security patches for the most recent few versions. Once your version falls off that list, newly found weaknesses stay open forever.
Vendors and add-ons move on
Cloud providers, networking plugins and storage drivers all test against recent versions. Running an old one means you are on your own when something breaks.
New abilities and fixes arrive
Each release adds improvements to scheduling, storage, security and stability that you can only use after upgrading.
Small steps are safer than big jumps
Skipping upgrades does not remove the work. It stacks the changes into one huge, risky leap that nobody on the team remembers how to do.
Why it feels scary
Teams fear upgrades for good reasons. The control plane is a shared dependency for everything. Some features get removed between versions, so a manifest that worked yesterday may be rejected tomorrow. Add-ons such as networking plugins have their own version rules. And stateful applications, such as databases, do not like having their machines taken away.
The good news is that almost every danger is predictable. Kubernetes publishes clear rules about which versions work together, which features are going away and in what order components must be changed. The skill is in learning those rules and turning them into a calm, repeatable routine.
The safest upgrade is a small, frequent, well-rehearsed one. If an upgrade feels like a special event, you are probably doing it too rarely.
Kubernetes Versions and the Release Rhythm
Understanding the numbers in a version tells you how risky a change is.
Reading a version number
Kubernetes uses a three-part number such as 1.31.4. The first part, 1, is the major version, and it has stayed at 1 for years. The middle part, 31, is the minor version and is the one that matters most. The last part, 4, is the patch version.
| Part | What it means | Typical risk | How often |
|---|---|---|---|
| Patch (1.31.4) | Bug and security fixes only, no new features | Low | Roughly monthly |
| Minor (1.31.4) | New features, changes, deprecations and removals | Medium to high, needs planning | About three times a year |
| Major (1.31.4) | Reserved for huge breaking changes | Not used recently | Never lately |
The release rhythm
The Kubernetes project follows a steady schedule. Roughly every four months a new minor version is released, and each one is supported with patches for about a year plus a couple of months to give people time to move. At any moment the project looks after the three most recent minor versions. When a fourth one arrives, the oldest falls out of support.
This means that if you installed a version and never touched it, you will be outside of support in a little over a year. There is no long-term support edition in the open source project. Some cloud vendors offer paid extended support, but that is only a delay, not an escape.
Think of milk in a shop. Every few days a fresh batch arrives, and the shop removes the oldest bottles from the shelf. You are free to keep a bottle at home for a long time, but nobody will vouch for it after the date on the label. Kubernetes versions have a label date too.
Patch upgrades versus minor upgrades
A patch upgrade, for example from 1.31.2 to 1.31.4, is usually uneventful. You can often apply it in a short window with the same process as a minor one but with far fewer surprises. A minor upgrade, from 1.30 to 1.31, is where the real planning is needed. You must read the release notes, check deprecated features, and test your add-ons.
One rule is absolute: you upgrade one minor version at a time for the control plane. To go from 1.29 to 1.32 you pass through 1.30 and 1.31 on the way. Skipping a minor version is not supported, because the project only tests the move from one version to the very next.
The deprecation policy
Kubernetes makes a promise about removing features. When something is going to be removed, it is first marked deprecated, which is a warning label meaning “stop using this, it will disappear”. The project then keeps it for a minimum period before removal. Stable (general availability) APIs are promised to last much longer than beta ones, while alpha ones can change or vanish quickly. Because of this promise, you get advance warning, but only if you read the notes and watch the warnings your cluster prints.
A deprecation warning is easy to ignore until the version where the feature is finally removed. Treat each warning as a small task on your to-do list, not as background noise.
What Actually Gets Upgraded
A cluster is many separate programs, and each has its own version and its own rules.
When people say “upgrade the cluster” they usually mean many different upgrades bundled together. It helps to list every piece, because forgetting one is a common mistake.
kube-apiserver
The front door of the cluster. Every command and every component talks through it. It is always upgraded first.
Controller manager and scheduler
The controller manager runs the loops that keep things in the desired state. The scheduler chooses which machine each new pod lands on.
etcd
The database that stores the entire state of the cluster. It has its own version and must be backed up before any serious change.
kubelet
The agent on each worker machine that starts and stops containers as instructed by the control plane.
kube-proxy
Handles network rules on each node so that Services can find their pods.
Container runtime
The program, such as containerd or CRI-O, that really runs containers. It has its own compatibility range.
Network, DNS and storage drivers
The network plugin, the DNS service called CoreDNS, and storage drivers all need to match the new Kubernetes version.
Operators, webhooks and custom resources
Third-party add-ons that extend Kubernetes. They may depend on API versions that change or disappear.
A hospital has surgeons, nurses, a records system, pharmacy deliveries and cleaners. Upgrading “the hospital” does not mean one change. It means updating each department in the right order so that patients keep receiving care. The control plane is the management office, the nodes are the wards, and the add-ons are the support services that everyone leans on.
Where the control plane lives
How you upgrade the control plane depends on how it was built. In a self-managed cluster, which you build yourself with tools such as kubeadm, you own the control plane machines and must upgrade them directly. In a managed cluster, such as Amazon EKS, Google GKE or Azure AKS, the cloud provider runs the control plane for you and upgrades it when you press the button or when your schedule says so. In both cases you still own the nodes and the add-ons, which is why even managed services need a careful routine.
Two kinds of state
It also helps to separate what the cluster is made of from what the cluster remembers. The programs can be replaced freely. The data in etcd, the cluster’s memory, cannot. Almost every safe upgrade plan therefore starts with the same step: take a verified backup of that memory before touching anything else.
Version Skew: Who Can Be Older Than Whom
During an upgrade, different parts of the cluster run different versions for a while. The skew rules say how different they may be.
What is version skew?
Because you cannot change everything at the same instant, your cluster will spend some time in a mixed state. For example, the control plane may already be on 1.31 while the nodes are still on 1.30. The difference in version numbers between two components is called skew. Kubernetes publishes a strict policy that describes the largest skew each pair of components can handle.
flowchart TB
API["kube-apiserver at version N - always upgraded first"]
CMS["controller-manager, scheduler, cloud-controller-manager: N or one older"]
KUB["kubelet: up to three minor versions older than N, never newer"]
PRX["kube-proxy: no newer than kubelet, within the kubelet range"]
CTL["kubectl: one minor older or newer than N"]
API --> CMS
API --> KUB
KUB --> PRX
API --> CTL
| Component | Allowed relative to kube-apiserver | Plain meaning |
|---|---|---|
| Other kube-apiserver copies (in a highly available control plane) | Within one minor version | While you roll the control plane, copies may differ by one step |
| kube-controller-manager, kube-scheduler | Same version or one older, never newer | Upgrade them after the API server |
| kubelet | Same or older by up to three minor versions, never newer | Nodes can lag behind, which gives you time to upgrade them |
| kube-proxy | Not newer than the API server and within the kubelet skew range | Keep it close to the kubelet on the same node |
| kubectl | One minor version newer or older | Keep your own tools current too |
Why the order follows the skew
These rules explain the upgrade order. Because the API server must never be older than the other pieces, it goes first. Because a kubelet must never be newer than the API server, nodes go after. Because nodes are allowed to be several versions behind, you do not have to upgrade every machine in one maintenance window. You can spread node upgrades over days or weeks.
Imagine a head office that sends instructions to branch offices. The head office can start using a new form, and the branches can keep using an older version for a while because the head office still understands it. But a branch must never start using a form that the head office has not learned yet. Newer-than-the-boss is the thing that is not allowed.
The three-version window is a gift, not a goal
The wide room for kubelets, which grew in recent releases, exists to give teams breathing space. It is not an invitation to leave nodes behind indefinitely. Old nodes miss security fixes, and some features simply do not work when nodes lag. Aim to finish node upgrades soon after the control plane, ideally within the same week.
The exact skew numbers have changed between releases, for example the allowed kubelet lag increased in a recent version. Always confirm the numbers in the official version skew policy for the version you are targeting before you plan.
The Upgrade Order and Lifecycle
There is a standard order of operations that nearly every Kubernetes upgrade follows. Learn it once and reuse it forever.
flowchart LR
A["1. Plan: read release notes and check deprecated APIs"] --> B["2. Back up etcd and key configuration"]
B --> C["3. Upgrade the control plane, one node at a time"]
C --> D["4. Upgrade cluster add-ons: network, DNS, proxy"]
D --> E["5. Upgrade worker nodes in small batches"]
E --> F["6. Validate: health checks, tests, dashboards"]
F --> G["7. Document, clean up and plan the next upgrade"]
Plan
Read the release notes for the target version, list removed and deprecated features, and confirm that your add-ons support the new version. Choose a time when traffic is low and when staff are available.
Back up
Take a snapshot of etcd and save copies of important configuration. Test that you can read the backup. A backup you have never tried to restore is only a hope.
Control plane
Upgrade the API server first, then the controller manager and scheduler, one control plane machine at a time, checking health after each step.
Add-ons
Upgrade or verify the network plugin, DNS, kube-proxy and similar components to versions that support the new Kubernetes release.
Nodes
Work through the worker machines in small batches. For each, move pods off safely, upgrade or replace it, and bring it back before starting the next batch.
Validate
Confirm all nodes report the new version, all system pods are healthy, test traffic passes, and your key dashboards look normal.
Wrap up
Record what happened, fix anything that surprised you, and update your runbook so the next upgrade is easier.
Where the order comes from
This order is not arbitrary. It is the direct result of the skew rules and of how components depend on each other. The API server is what everything else talks to, so it must be able to understand both old and new clients. Nodes only need to speak to the API server, so they can follow later. Add-ons sit in between because they depend on both sides.
Practice the full sequence on a development cluster, then a staging cluster that resembles production, and only then production. Every problem found early is a problem your customers never see.
Before You Upgrade: The Readiness Checklist
Most upgrade failures are decided before the first command is typed. Preparation is where the real work lives.
Read the release notes
Every Kubernetes release comes with notes that describe new features, deprecations, removals and “urgent upgrade notes” that call out changes requiring action. Read the sections for every minor version you are crossing. It takes an hour and saves days. Look especially for removed APIs, changed defaults, and changes in the behavior of the kubelet and container runtime.
The checklist
| Area | Question to answer | Why it matters |
|---|---|---|
| Deprecated APIs | Does anything we run use an API version that is removed in the target? | Removed APIs make deployments and tools fail immediately |
| Add-on compatibility | Does every add-on list support for the new version? | Unsupported networking or storage drivers can break pods |
| Backups | Do we have a recent etcd snapshot, and have we tested a restore? | It is your only real way back from a bad control plane failure |
| Capacity | Can the cluster run its workload with one batch of nodes missing? | Draining nodes shifts load onto the others |
| Disruption budgets | Are PodDisruptionBudgets present and reasonable? | Too strict blocks the drain, too loose lets too many pods vanish |
| Certificates | When do cluster certificates expire? | Expired certificates cause sudden outages, and upgrades are a good time to renew |
| Webhooks | Are admission webhooks healthy and tolerant of restarts? | A broken webhook can block new pods from starting |
| People and timing | Who is on call, and is there a freeze on other changes? | Surprises multiply when many changes happen together |
| Rollback plan | What will we do if step three fails? | Decide while calm, not in the middle of an incident |
Check the health of the cluster first
Never start an upgrade on a cluster that is already unhealthy. If some nodes are not ready, some system pods are crashing or etcd is complaining, fix those first. Otherwise you will not know whether a problem after the upgrade was caused by the upgrade or was already there. Take a note of the state before you begin: how many nodes, how many pods, which alerts are firing. This becomes your “before” picture.
Before a long road trip you check the tires, the oil and the fuel, and you plan where to stop. You do not set off with a warning light already glowing on the dashboard. A readiness checklist is your pre-trip inspection.
Communicate
Tell the teams that run applications on the cluster what is coming and when. Share the plan, the time window, what they might notice, and how to reach you. Good communication turns a surprise into a scheduled event, and it gives application owners a chance to say “please do not do this during our launch”.
Keep the checklist in version control as a living document. Each upgrade, add the lesson you learned. After a few rounds you will have a runbook tailored to your own cluster.
Deprecated and Removed APIs
The number one cause of broken upgrades is a feature that was removed while someone was still using it.
What is an API version?
Every kind of object in Kubernetes, such as a Deployment or an Ingress, is described by an API group and version, written at the top of its definition. As features mature, their version changes from a beta label to a stable one. When the stable version is ready, the older beta version is deprecated and, after a waiting period, removed from the API server.
If your configuration files, deployment tools or add-ons still use a removed version, the API server will simply refuse them. The failure can be sudden: your next deployment fails, an operator crashes, or a pipeline that has worked for years turns red.
Famous removals
| Version | What was removed | What to use instead |
|---|---|---|
| 1.22 | Beta versions of Ingress and several other older APIs | The stable networking Ingress API |
| 1.24 | Dockershim, the built-in bridge to Docker Engine | containerd or CRI-O as the container runtime |
| 1.25 | PodSecurityPolicy and the beta CronJob API | Pod Security Admission, and the stable batch CronJob API |
| 1.26 | Older beta versions of the HorizontalPodAutoscaler | The stable autoscaling v2 API |
| 1.29 | Older beta versions of flow control APIs | The newer flowcontrol API versions |
These examples show that removals come in many flavors. Some are plain version changes in a configuration file. The dockershim removal was a deeper change that forced teams to migrate their container runtime on every node. The removal of PodSecurityPolicy required a new approach to security rules altogether.
It is like a country changing its electrical plug standard. Appliances you own may still be perfectly good, but they will not fit the new sockets. A warning period lets you buy adapters or new appliances, but only if you notice the notice.
How to find what you are using
API server metrics and warnings
The API server counts requests made to deprecated APIs in a metric, and prints warnings to clients. Watching these over a few weeks shows real usage, including from controllers you forgot about.
Pluto and kube-no-trouble
Open source tools that scan running resources, manifest files and Helm releases and report deprecated or removed API versions.
Schema checkers in your pipeline
Tools such as kubeconform validate manifests against the schemas of the target version, so a mistake is caught in review, not at deploy time.
kubectl convert plugin
Helps rewrite old manifests to newer API versions, saving manual editing.
Things people forget
- Helm releases: Helm stores the manifests of each release inside the cluster. If those stored manifests use a removed API, Helm may fail to upgrade or roll back that release. A community plugin exists to rewrite them.
- Operators and controllers: A tool installed years ago may still call an old API. Check vendor notes and update them before the control plane.
- CI and deployment tools: Pipelines, templates and internal scripts often hold their own copies of old manifests.
- Stored objects: Data in etcd is saved in a particular version. When an old version is going away, a migration step rewrites stored objects into a newer form so nothing is left unreadable.
Update manifests and tools to the new API versions before the control plane upgrade, while the old version still works. Both versions are usually served together for a while, which makes this a calm change instead of an emergency.
Upgrading the Control Plane
The control plane is the brain of the cluster. The goal is to change it without ever letting the cluster lose its mind.
Why the control plane is special
While the control plane is briefly unavailable, your existing applications keep running, because the kubelet on each node continues to look after the containers it already has. What stops working is change: you cannot deploy, scale, or replace failed pods, and nothing reacts to events. A short control plane pause is usually harmless, but a long one is a serious risk, especially if a node fails during that time.
Picture an air traffic control tower being renovated. Planes already in the air keep flying along their agreed routes, and pilots keep following their instructions. But no new flights can be coordinated until the tower works again. The tower should therefore be renovated with a second tower standing by.
High availability makes it safe
A production control plane normally runs on three machines, each with a copy of the API server, the controller manager, the scheduler and, in the common layout, etcd. Because there are three copies, you can take one down while the other two continue serving. This is what makes a rolling control plane upgrade possible with no downtime.
There is one rule to respect here. etcd uses a voting system called quorum, meaning a majority of members must agree. With three members, two must be healthy. If you take one down for upgrade and a second one fails at the same time, the cluster freezes. So you upgrade one control plane machine at a time and wait until it is fully healthy before touching the next.
The typical sequence with a self-managed cluster
Upgrade the tooling
Install the new version of the cluster management tool, for example kubeadm, on the first control plane machine. Run its planning command, which checks that the cluster can move to the target version and lists the components and versions it will change.
Apply to the first control plane node
The tool upgrades the API server, controller manager, scheduler and, if it manages them, etcd and the cluster DNS and proxy add-ons. It also renews the control plane certificates by default, which is a useful side benefit.
Upgrade the node agent on that machine
Move the machine’s pods away, upgrade the kubelet and command line tool packages, restart the kubelet, and mark the machine schedulable again.
Repeat on the other control plane machines
For each remaining machine, use the lighter “upgrade node” form of the command, then update its kubelet in the same way. Check health between each machine.
Verify
Confirm that every control plane component reports the new version, etcd has a healthy quorum, and the API server answers quickly.
Small but important details
- Load balancer: The load balancer in front of the API servers should stop sending requests to a machine that is being upgraded. Make sure its health checks work.
- Webhooks: If admission webhooks that are configured to fail closed point at services on the same cluster, a restart can briefly block object creation. Know which ones you have.
- Leader election: The controller manager and scheduler elect one active copy, so brief changes of leader are normal during the rollout.
- Managed control planes: In a managed service, this whole chapter becomes one action in the provider’s console or tooling, but the provider still expects you to prepare in the same ways and to upgrade your own nodes afterwards.
The control plane upgrade is the step with the fewest machines but the largest impact. Take your time, check health after each machine, and never run two risky actions at once.
Upgrading Worker Nodes: Cordon, Drain, Uncordon
Worker nodes are where your applications live. Upgrading them safely means moving the residents out gently before the walls are repainted.
Three verbs to remember
Node upgrades use a small vocabulary of three actions. Once you understand them, every node strategy in this guide is just a variation on the same theme.
Cordon
Mark the node as unschedulable. Existing pods stay put, but no new pods are placed there. Think of it as hanging a “no new guests” sign on the door.
Drain
Politely ask every pod on the node to leave. Kubernetes evicts them and their controllers start replacements on other nodes.
Uncordon
After the node is upgraded and healthy, remove the sign so that new pods may be placed there again.
sequenceDiagram
participant OP as Operator or Automation
participant API as API Server
participant OLD as Node being upgraded
participant SCH as Scheduler
participant OTH as Other healthy nodes
OP->>API: Cordon the node
API->>OLD: Mark as unschedulable
OP->>API: Drain the node
API->>API: Check PodDisruptionBudgets for each pod
API->>OLD: Evict pod with a graceful shutdown
OLD-->>API: Pod has stopped
API->>SCH: Controller creates a replacement pod
SCH->>OTH: Place replacement on a healthy node
OTH-->>API: Replacement is ready
OP->>OLD: Upgrade kubelet and restart it
OP->>API: Uncordon the node
What happens during a drain
A drain does not simply delete pods. It uses the Eviction API, a polite request that the API server checks against any PodDisruptionBudget (PDB) before allowing it. A PDB is a rule that says how many copies of an application must stay available at any time. If removing a pod would break that rule, the eviction is refused and the drain waits and retries.
Once a pod is evicted, it receives a termination signal and gets a grace period to finish its work, 30 seconds by default. Well-written applications use this time to stop taking new requests, finish current ones, close connections and exit. Then the owning controller, such as a Deployment, notices a missing replica and creates a new pod, which the scheduler places on another node.
Draining a node is like closing a restaurant section for cleaning. The host stops seating new tables there (cordon), waiters help current diners finish their meals and move to other sections (drain), and when cleaning is done the host opens the section again (uncordon). A rule that says “at least half the tables must stay open” is a PodDisruptionBudget.
What a drain does not save
- DaemonSet pods run one per node by design, so a drain skips them. You normally tell the command to ignore them.
- Bare pods that were created directly without any controller are not recreated elsewhere. They are gone once evicted, so avoid using them in real workloads.
- Local temporary storage in a pod is erased when it leaves. The drain command requires you to acknowledge this explicitly.
- Pods that ignore shutdown signals will be force killed after the grace period, which can drop in-flight requests.
Upgrading the node itself
With the node empty, you upgrade its software: the kubelet, the container runtime if required, and often the operating system too. Then restart the kubelet, wait until the node reports ready on the new version, and uncordon it. Many teams use this moment to also apply operating system patches, since the machine is already empty and will restart anyway.
Use a drain timeout so that automation does not hang forever on a stubborn pod. When a drain times out, a human or a smart workflow should decide what to do, rather than blindly forcing the pod off.
Node Upgrade Strategies Compared
There are three main ways to move a fleet of nodes to a new version. Each trades speed, cost and safety differently.
Strategy 1: In-place upgrade
With an in-place upgrade, you take each existing machine, drain it, upgrade its software while it is still the same machine, and put it back. This is what traditional kubeadm-style workflows do. It needs no extra machines, so it costs nothing extra, but it reduces your capacity by one machine (or one batch) at a time, and any drift on old machines comes along for the ride.
Strategy 2: Rolling replacement with surge
With rolling replacement, you do not upgrade old machines at all. Instead you create fresh machines already running the new version, move workloads over, and then delete the old machines. Adding extra machines before removing old ones is called surge, and it means capacity never dips. Most managed services and infrastructure-as-code setups prefer this approach because the new machines come from a clean, known image.
Strategy 3: Blue/green node pools
In a blue/green node pool upgrade, you create an entire second group of nodes (the green pool) on the new version beside the old (blue) pool. Then you cordon and drain the blue pool in controlled steps, shifting everything to green. If something looks wrong, you can pause, and in many cases move back, because the old nodes still exist. The price is running double capacity for a while.
| Strategy | Extra cost | Speed | Safety | Best for |
|---|---|---|---|---|
| In-place | None | Medium | Medium, capacity dips during each batch | Small or on-premises clusters with no spare machines |
| Rolling with surge | A little, for the extra nodes | Fast | High, capacity stays steady | Most cloud clusters and managed node groups |
| Blue/green node pools | Double nodes during the move | Slower, with pauses | Highest, easy to pause and compare | Critical workloads where mistakes are very costly |
Renovating a hotel offers three plans. In-place is repainting rooms one at a time while guests stay in the others. Rolling replacement is building a few spare rooms first, so you never run short. Blue/green is constructing a whole second wing, moving everyone across floor by floor, and only then demolishing the old wing.
Choosing batch size
All three strategies need a decision about how many nodes to change at once. Cloud services expose this as settings such as max surge (how many extra nodes may be created) and max unavailable (how many may be down at once). A small batch is safer but slower. A good starting point is to keep the number of unavailable nodes small enough that the rest of the cluster can comfortably carry the load, and then increase it once you trust your tests. Use one node first as a canary, check that workloads behave, and only then continue in larger batches.
Immutable nodes and drift
The industry is moving toward immutable nodes, which means you never patch a machine in place but replace it with a freshly built one. This avoids configuration drift, the slow buildup of small differences between machines that makes behavior hard to predict. Tools that automatically replace nodes whose image or version no longer matches the desired state, such as Karpenter’s drift handling, make this a natural fit for Kubernetes upgrades.
Whichever strategy you choose, new nodes must obey the skew rules. They should run a kubelet version no newer than the control plane. Upgrade the control plane first, then build new nodes to match it.
Blue/Green Cluster Upgrades
Some teams skip in-place upgrades altogether. They build a brand new cluster and move traffic across.
The idea
Instead of changing a running cluster, you create a second cluster on the new version, deploy all your applications to it from the same source of truth, test it, and then gradually shift real user traffic. The old cluster stays alive until you are completely happy. If something goes wrong, you send the traffic back. This is sometimes called the blue/green cluster or cluster replacement pattern.
flowchart TB
USR["Users"] --> GLB["DNS or Global Load Balancer"]
GLB -->|"most traffic at first"| BLUE["Blue cluster at the current version"]
GLB -.->|"small share, then growing"| GREEN["Green cluster at the new version"]
REPO["Git repository with all desired configuration"] --> BLUE
REPO --> GREEN
BLUE --> DATA["Shared databases, queues and storage"]
GREEN --> DATA
Advantages
- Skips the risky in-place changes to the control plane
- Allows long, careful testing with real traffic
- Rollback is as simple as moving traffic back
- Lets you jump further than one minor version if you rebuild properly
- Naturally removes drift and old, forgotten configuration
Disadvantages and trade-offs
- Costs double for a period
- Requires everything to be reproducible from code
- State is hard: databases, queues and volumes need a plan
- Needs a traffic switching layer outside the cluster
- External addresses, certificates and allow-lists may change
What makes it work
Blue/green clusters only work when your platform is built for it. All configuration must live in version control and be applied automatically, a practice known as GitOps. Secrets, DNS records, certificates and load balancers must be created by automation rather than by hand. And any state that lives inside the cluster, such as persistent volumes, must either be moved with a tested process or kept outside the cluster in managed data services that both clusters can reach.
A restaurant opens a new kitchen next door with the same recipes and the same suppliers. For a week, only a few tables are sent to the new kitchen. When diners are happy, more tables move. The old kitchen stays ready until the owner is certain. The recipes (your code) are what make the move easy, as long as the pantry (your data) can be shared.
When to choose it
Blue/green clusters shine when the cost of a failed upgrade is very high, when your clusters are numerous and mostly stateless, or when you want to move several versions at once without a long chain of in-place steps. For a small team with a single stateful cluster, in-place or surge upgrades are normally simpler and cheaper.
Protecting Your Workloads During the Change
The cluster upgrade is only as smooth as the applications running on it are well behaved.
Draining a node is routine for Kubernetes, but it will expose any application that was not built to move. The best time to find out is before the upgrade. Here are the settings that decide whether your pods leave quietly or noisily.
Run more than one copy
A single replica means the application is down every time its node is drained. Run at least two or three for anything that matters.
Set a sensible minimum
Choose a number that keeps the service healthy but still leaves room to evict at least one pod. A rule that demands every replica always be available blocks every drain forever.
Use topology spread rules
Spread copies across different nodes and zones so a drain never removes all replicas at once.
Accurate readiness checks
A replacement pod should only receive traffic when it is truly ready. Otherwise, the drain “succeeds” while users receive errors.
Handle termination signals
Stop accepting new work, finish current requests, and exit before the grace period ends. A short pause before shutdown gives load balancers time to stop routing to the pod.
Start quickly
Pods that take minutes to start slow every node drain. Smaller images and efficient start-up shorten the whole upgrade.
The trap of the too-strict budget
One of the most common upgrade blockers is a PodDisruptionBudget that cannot be satisfied. Suppose a Deployment has three replicas and its PDB says that three must always be available. Kubernetes can never evict even one pod without breaking that promise, so the drain waits forever. The same happens when a single-replica application has a budget requiring one pod to always be available. Check your budgets before the upgrade by looking for any whose allowed disruptions equal zero.
It is like a rule in a hospital that says “all ten nurses must be on duty at every minute of the day”. The rule sounds safe, but no nurse could ever take a break or go home, and the hospital would grind to a halt. A good rule says “at least eight must be on duty”, which leaves space for people to rotate.
Capacity during the move
While a node is empty, its pods have moved elsewhere. If the remaining nodes are already nearly full, replacement pods will remain Pending, the drain will stall, and your services will run on reduced capacity. Make sure there is enough spare room to lose a batch of nodes. Surge nodes, which add capacity before removing it, are the standard cure.
Drain one non-critical node in a test cluster and watch what happens. Every surprise you see there, such as pods that will not leave or services that return errors, is one fewer surprise during the real upgrade.
Stateful Workloads and Storage
Databases, message brokers and anything that remembers data need extra care when machines come and go.
Why state changes the game
A stateless web server can be thrown away and recreated in seconds, because it holds nothing important. A database holds the only copy of something precious, so moving it means moving data, replicating data, or at least reattaching a disk. Each of those steps takes time and can go wrong, which is why stateful workloads deserve their own upgrade plan.
Moving a stateless service is like moving a rented chair: pick it up and place it anywhere. Moving a database is like moving a grand piano. It needs the right people, the right route and a firm plan, and you do not hurry it.
Key issues to plan for
| Concern | What can happen | What to do |
|---|---|---|
| Volumes tied to a zone | A disk exists in one availability zone, so its pod can only run in that zone. A replacement node in another zone leaves the pod Pending. | Make sure surge or new nodes exist in every zone that holds volumes |
| Volume detach and attach time | Moving a disk between machines can take a minute or more | Allow generous timeouts and avoid draining several stateful pods together |
| Local disks | If data is stored on a node’s own disk, replacing the node destroys it | Use replication, and replace nodes one at a time while replicas catch up |
| Quorum-based systems | Taking too many members of a cluster down at once breaks consensus | Use PodDisruptionBudgets that preserve a majority |
| Primary and replica roles | Draining the primary forces a failover at an awkward moment | Upgrade replicas first and fail over to a healthy one in a planned way |
| Storage drivers | Storage plugins have version rules too | Upgrade or verify drivers before nodes |
StatefulSets and their special ordering
A StatefulSet is the Kubernetes object designed for applications that need stable identities and storage. It updates pods one at a time in a fixed order and waits for each to be healthy before continuing. It also has a setting called partition that lets you update only some pods first, which is a convenient way to try a change on one replica before touching the rest.
Operators help, but verify
Many databases run under an operator, a piece of software that knows how to run that particular database on Kubernetes, including safe failovers. A good operator makes node drains much safer. But the operator is itself a program with API dependencies, so confirm its supported Kubernetes versions before the upgrade and read its notes on node maintenance.
An etcd snapshot protects the cluster’s configuration. It does not protect your database contents. Make sure application-level backups are fresh and restorable before an upgrade that touches stateful nodes.
Add-ons, Networking and the Container Runtime
The extras you install around Kubernetes have their own version rules, and they cause a large share of upgrade trouble.
Add-ons are part of the cluster
A bare Kubernetes installation does little. Real clusters add a network plugin (also called CNI) so pods can talk to each other, CoreDNS so that names resolve, kube-proxy for service routing, storage drivers (CSI) to create volumes, an ingress controller to receive outside traffic, certificate managers, monitoring agents and often a service mesh. Every one of these has a compatibility table that says which Kubernetes versions it supports.
CNI plugin
If the network plugin is incompatible, pods lose connectivity. Upgrade it according to its own guide, usually before or with the control plane.
CoreDNS
Every service lookup depends on it. Run several replicas so DNS stays up when pods move during node drains.
CSI drivers and snapshot controllers
Needed to create, attach and expand volumes. Check support for the target version.
Ingress and gateways
Older controllers may depend on removed APIs. Test routes, certificates and rules after the upgrade.
Service mesh proxies
Mesh components often require specific Kubernetes ranges and must be upgraded in their own careful order.
CRDs and controllers
Custom resource definitions and their controllers need compatible versions on both sides of the upgrade.
The container runtime
The container runtime is the engine that actually starts containers on each node. Kubernetes talks to it through a standard interface. For many years some clusters used Docker through a built-in bridge called dockershim. That bridge was removed in version 1.24, so clusters that depended on it had to move to containerd or CRI-O first. This change is a good reminder to check runtime versions, cgroup settings and any tools that expected to find the Docker socket on a node, such as older log collectors or build agents.
If the cluster is a city, the add-ons are the utilities: roads, water, electricity and mail. When you rebuild the city hall, you must also make sure the utilities still connect to it. Nobody notices good utilities, but everyone notices a day without water.
The safe approach to add-ons
- Make an inventory of every add-on, with its current version and its supported range.
- Read each project’s upgrade notes for the target Kubernetes version.
- Upgrade add-ons that must move first, as their guides specify, and leave the rest until after the control plane.
- Run tests that touch each add-on: a pod to pod connection, a DNS lookup, a volume creation, an ingress request.
- Keep this inventory in version control so the next upgrade starts from a ready list.
Some add-ons must be upgraded before the control plane, and others after. There is no universal answer, so always follow the add-on’s own documentation rather than assuming.
Managed Kubernetes and Cluster API
Cloud services and automation frameworks take over much of the effort, but not all of the responsibility.
Managed services
In services such as Amazon EKS, Google GKE and Azure AKS, the provider operates the control plane. You request a new version, and the provider upgrades the control plane machines, usually with no downtime for your workloads. You remain responsible for the nodes, the add-ons and your applications. Each provider has its own way of expressing policy.
| Service | How versions and upgrades are handled | Things to know |
|---|---|---|
| Amazon EKS | You start a control plane upgrade one minor version at a time. Managed node groups can roll nodes with a configurable number unavailable. Core add-ons such as the VPC CNI, CoreDNS and kube-proxy are updated separately. | Versions have a standard support period, and a longer paid extended support option exists. Update add-ons as part of the plan. |
| Google GKE | Clusters follow release channels such as Rapid, Regular, Stable and Extended, with automatic upgrades. Node pools support surge upgrades and blue/green upgrades. | Use maintenance windows and exclusions to avoid upgrades during busy periods. |
| Azure AKS | You can choose automatic upgrade channels for new versions or for patches only, and a separate channel for node images. Max surge controls how many extra nodes are used. | Cluster and node pool versions are upgraded as separate steps. |
Using a managed service is like renting a flat in a building with a caretaker. The caretaker looks after the lifts, the roof and the plumbing in the shared areas. But you are still responsible for your own furniture and for telling the caretaker when you will be out. The shared parts being cared for does not mean your own belongings are safe from the move.
Release channels and auto-upgrade
Letting the platform upgrade automatically is a good idea for non-production clusters and often for production too, as long as you control the timing. A release channel lets you choose how early you want to receive new versions. Early channels give you access to features sooner but with less time-tested software. Later channels favor stability. Pair them with maintenance windows, and test in an early-channel staging cluster before the same version arrives in production.
Cluster API: upgrades as a declaration
Cluster API is a Kubernetes project that treats clusters themselves as Kubernetes objects. You describe the cluster and its machine groups in YAML-style objects, and controllers create and maintain the real machines. To upgrade, you change the declared version on the control plane object, then on each machine group, and the controllers perform a rolling replacement of machines on your behalf. It turns the upgrade from a long manual runbook into a reviewed code change.
What you still own
- Checking removed APIs and add-on compatibility.
- Upgrading node groups and verifying that applications tolerate the move.
- Choosing timing, batch sizes and the disruption budgets that protect your services.
- Testing and monitoring, before, during and after.
Managed control planes generally cannot be downgraded once upgraded. Treat the upgrade as a one-way door and make your testing count before you walk through it.
Security During and After Upgrades
Upgrades are one of the best security tools you have, and also a moment when mistakes in access and process can hurt.
Why upgrading is a security practice
When researchers find a weakness in Kubernetes or in the software that runs underneath it, the fix is released in a new patch version of the supported releases. If your cluster is not on a supported version, there will be no fix for you. Falling behind on patches is the same as leaving a known unlocked door in your building. Regular patch upgrades keep that door shut at low risk, and minor upgrades keep you inside the support window.
A locksmith announces that a certain brand of lock can be opened with a paperclip and sends out replacements. If you do not change your lock, the announcement helps burglars more than it helps you, because now everyone knows the trick. Public security fixes make unpatched systems easier targets.
Limit who can upgrade
Upgrade rights are extremely powerful. Use role-based access control, short-lived credentials and approvals for production changes.
Trust the source
Install packages and images only from official or verified repositories, and check signatures and checksums.
Renew while you are there
Cluster certificates expire, often after a year. Upgrading with kubeadm renews control plane certificates, which prevents a surprise outage on the expiry date.
Replace removed controls
When a security feature is removed, such as PodSecurityPolicy, make sure its replacement, like Pod Security Admission, is already enforcing equivalent rules before the old one disappears.
Patch the operating system too
The node upgrade is a convenient moment to apply operating system and runtime patches, using fresh images where possible.
Keep a record
Enable audit logs and record who approved and executed each upgrade step. These records matter for both investigations and compliance.
Security can break during an upgrade
Changes in defaults between versions can quietly alter your security posture. A new default might tighten something that used to be open, breaking an application, or an admission rule might stop being enforced after an API change. After every upgrade, run a short security smoke check: confirm that network policies still block what they should, that role bindings are unchanged, that admission controls are active, and that audit logging continues to flow.
Many teams treat patch upgrades as a quick routine to apply within days of release, and plan minor upgrades on a regular quarterly rhythm. This keeps them safe without making every update a big event.
Monitoring and Validating the Upgrade
You cannot manage what you cannot see. During an upgrade, the right dashboards turn fear into facts.
What to watch while it happens
An upgrade is a short period when the system is deliberately changing, so normal alerts may fire and normal patterns may shift. Prepare a single dashboard that shows the vital signs of both the cluster and the applications on it. Watch it live during each step, and compare it with the “before” picture you recorded.
| Signal | Healthy during upgrade | Warning sign |
|---|---|---|
| API server request latency and error rate | Brief blips while a control plane node restarts | Sustained errors or very slow responses |
| etcd health and leader changes | One leader change when its host restarts | Repeated elections or slow disk writes |
| Node readiness count | One batch temporarily NotReady or cordoned | Nodes staying NotReady after the upgrade |
| Pending and crash-looping pods | A short spike while pods move | Pods stuck Pending or restarting repeatedly |
| Application error rate and latency | Flat or a tiny blip | Rising errors correlated with drains |
| DNS and network checks | Always passing | Any failed lookup or connection |
| Deprecated API request metric | Falling to zero over time | New callers appearing |
flowchart LR
CP["Control plane metrics"] --> PROM["Prometheus or monitoring platform"]
ND["Node and kubelet metrics"] --> PROM
APPS["Application and SLO metrics"] --> PROM
SYN["Synthetic checks and smoke tests"] --> PROM
PROM --> DASH["Upgrade dashboard"]
PROM --> ALRT["Alert routing"]
DASH --> ENG["Engineer running the upgrade"]
ALRT --> ENG
Smoke tests after each stage
A smoke test is a small, fast check that the basics still work. Run a handful after the control plane, after the add-ons and after each node batch. A good set includes creating and deleting a test deployment, resolving a service name through DNS, calling one service from another, creating a volume and attaching it to a pod, and reaching your application through its public address. Automate these so that they run the same way every time.
After a plumber works on a house, the owner turns on every tap, flushes every toilet and checks for leaks before paying the bill. Nobody waits for a flood to discover a missed pipe. A smoke test is that quick walk around the house.
Deeper validation
For major platform changes, you can also run the Kubernetes conformance tests, using a tool such as Sonobuoy. These are a large suite of tests that check whether a cluster behaves like a standard Kubernetes should. They do not replace your own application tests but they catch problems in the platform itself. Finish by comparing your final state with the “before” picture: same number of nodes, same set of healthy pods, no new alerts.
Write down what “finished” means before you begin: every node on the new version, all system pods healthy, smoke tests green, error rates normal for a set time. This stops you from declaring victory too early.
Rollback and Failure Recovery
Hope for a smooth upgrade, and plan for a rough one. The most important question is what you will do when a step fails.
The uncomfortable truth about downgrades
Kubernetes does not offer a supported, general way to downgrade a control plane to an earlier minor version. After the upgrade, data in etcd may be written in newer forms, and older components may not understand it. So “rollback” in Kubernetes rarely means flipping a switch back. It means one of a few different recovery paths, depending on what failed.
flowchart TD
P["Problem seen during upgrade"] --> Q1{"Is the control plane healthy?"}
Q1 -->|"No"| R1["Stop, diagnose, use the tool's own recovery or restore the etcd snapshot"]
Q1 -->|"Yes"| Q2{"Are nodes or workloads failing?"}
Q2 -->|"Yes"| R2["Pause the rollout, keep or restore the old node pool, move workloads back"]
Q2 -->|"No"| R3["Continue with the next small batch and keep watching"]
Recovery paths by failure type
| What failed | Realistic recovery | Notes |
|---|---|---|
| A control plane step fails part way | Fix the cause and re-run. Tools like kubeadm back up the control plane manifests and attempt to roll back automatically on failure. | Remaining healthy control plane machines keep serving meanwhile |
| Control plane is broken beyond repair | Restore etcd from the snapshot taken before the upgrade and rebuild the control plane at the old version | Changes made after the snapshot are lost, so move quickly and freeze other changes |
| A batch of nodes misbehaves | Stop the rollout. Replace those nodes with ones built from the previous known-good image, or move workloads back to the old node pool | Keeping the old pool alive makes this fast |
| An add-on breaks | Roll the add-on back to its previous version, as add-ons are normal workloads | This is usually possible even after the control plane moved |
| An application breaks because of an API change | Fix the manifest or roll the application back to a previous release | The cluster stays on the new version |
| Managed control plane upgrade has a problem | Open a provider support case and work around the issue on the node and application side | The provider cannot usually downgrade it |
A pilot cannot turn a plane around once it has passed the point of no return. So before takeoff a pilot knows exactly where the last safe turn-back point is, what to do if an engine fails before it and what to do after it. An upgrade plan has the same kind of point of no return: the control plane upgrade. Everything before it is easy to reverse, and everything after needs a different recovery plan.
Practice the restore
Restoring etcd is a rare operation with many fine details, such as restoring every member consistently and re-pointing components at the restored data. The worst time to learn those details is during an outage. Rehearse the restore on a throwaway cluster at least once a year, write the steps down, and time how long it takes. You will then know your real recovery time instead of guessing.
Design for reversible steps
The safest upgrade plans keep the largest number of steps reversible for the longest time. Keep old node pools until the new ones have carried production traffic. Keep old cluster copies until blue/green traffic has fully moved. Delay deleting backups. Make decisions you cannot undo as late in the process as possible, and only after validation passes.
An etcd snapshot is only useful for the state it captured. If you take it a day before the upgrade and then change things, you will lose those changes on restore. Take the snapshot immediately before you begin and record the time.
Design Patterns and Anti-patterns
Teams with years of upgrade experience share a short list of habits and a longer list of regrets.
Patterns that work
Small and often
Upgrade every minor version soon after it stabilizes, instead of waiting a year. Each step is small and practiced.
Promote through environments
Dev first, then staging, then a canary slice of production, then the rest. Every stage uses the same automation.
Canary node first
Upgrade one node, watch it carry real work, then continue in growing batches.
Everything as code
Cluster definitions, add-ons and policies live in version control, so an upgrade is a reviewed change with history.
Immutable, replaceable nodes
Build new node images for each version and replace old nodes, instead of patching them one by one.
Automated pre-flight checks
Pipelines that scan for deprecated APIs and check add-on compatibility fail early, long before upgrade day.
Anti-patterns to avoid
The mistake
Postponing upgrades until the version is about to leave support, then doing several in a row.
Why it hurts
Changes pile up, nobody remembers the process, and a single mistake affects a large number of skipped versions.
Better approach
Adopt a regular cadence, such as one minor upgrade per quarter, so the work is routine.
The mistake
Testing only on an empty development cluster that looks nothing like production.
Why it hurts
Real problems come from real add-ons, real policies, real load and real quirks, none of which an empty cluster has.
Better approach
Keep a staging cluster that mirrors production’s add-ons, policies and workload types, and run realistic tests there.
The mistake
Forcing drains with flags that ignore disruption budgets to “get it done faster”.
Why it hurts
You bypass the very protection that keeps services up, and may remove every replica of an application at once.
Better approach
Fix the budget or the application, then drain normally. Treat forced drains as an exceptional action with approval.
The mistake
Upgrading the cluster and an application platform change on the same day.
Why it hurts
If something breaks, you cannot tell which change caused it, and the rollback of one may not fix the other.
Better approach
Freeze unrelated changes around upgrade windows so that cause and effect stay clear.
The mistake
Treating nodes as pets: logging in, patching by hand and keeping different versions on different machines.
Why it hurts
Each machine becomes unique, which makes upgrades unpredictable and hard to repeat.
Better approach
Build nodes from images and automation, and replace them rather than repair them.
Troubleshooting and Common Mistakes
When an upgrade stalls, the cause is nearly always on a short and familiar list.
| What you see | Likely reason | What to do |
|---|---|---|
| Drain hangs forever | A PodDisruptionBudget allows zero disruptions, or a pod refuses to stop | Review budgets, check which pod is blocking, fix the application or budget |
| Replacement pods stay Pending | Not enough capacity, or volumes tied to a zone with no available node | Add surge capacity in the correct zones |
| Deployments or tools suddenly fail | A removed API version is still used in manifests or pipelines | Update manifests, use scanners and re-run |
| Nodes stay NotReady | Network plugin, runtime or kubelet configuration is incompatible with the new version | Check kubelet logs and the add-on compatibility notes |
| Pod creation fails cluster-wide | An admission webhook is unavailable and set to fail closed | Restore the webhook service or adjust its failure policy carefully |
| DNS lookups fail | CoreDNS pods not running or mismatched configuration | Check CoreDNS pods and configuration, restart if needed |
| Certificates rejected after upgrade | Expired or unrenewed certificates, or clock differences | Renew certificates and check the time on all machines |
| kubectl warns about version differences | Your client is more than one minor version from the server | Install a matching kubectl |
| Workloads behave oddly but everything is “green” | A changed default, such as scheduling or security behavior | Compare against the release notes and test the affected feature |
A useful order of investigation
Look at the control plane
Is the API server answering, and are its logs showing errors? Is etcd healthy with all members present?
Look at the nodes
Which nodes are NotReady or cordoned? What do the kubelet and runtime logs on those nodes say?
Look at system pods
Check the network plugin, DNS, proxy and storage drivers. A broken add-on often looks like a broken cluster.
Look at events
Cluster events state, in plain language, why pods fail to schedule or start.
Look at applications
Only after the platform is understood, check specific application symptoms and compare them with the timeline of node drains.
Write down what you did and when, as you do it. A simple running log makes it far easier to link a symptom to a step and to write a clear report afterwards.
Real-World and Industry Examples
Real incidents and real platforms show how these ideas play out in practice.
Reddit’s Pi Day outage
In March 2023, Reddit suffered a long outage and later published a detailed account. As they described it, the trigger was an upgrade from Kubernetes 1.23 to 1.24, in which a node label that their networking setup relied on had been renamed. Components that selected nodes by the old label no longer matched, and the network configuration broke. It is a perfect lesson in reading release notes closely, testing against production-like settings, and keeping a way back.
The Kubernetes project and the dockershim removal
The removal of dockershim in 1.24 was announced and discussed for a long time, with deprecation warnings and community guides on migrating to containerd and CRI-O. Teams that heeded the warning moved quietly, while teams that ignored it discovered the change on upgrade day. It shows how the deprecation policy rewards those who read the notices.
Google Kubernetes Engine: automation with guardrails
GKE gives teams release channels, automatic upgrades, maintenance windows and exclusions, and both surge and blue/green node pool upgrades. This is a good model of what a mature upgrade system looks like: automatic by default, controlled by policy, and easy to pause for important events.
Amazon EKS and Azure AKS: shared responsibility
Both services take over the control plane, and both leave the nodes and many add-ons in the customer’s hands. Their documentation stresses the same duties covered in this guide: check removed APIs, update add-ons, and roll node groups with controlled surge settings.
Cluster API users: upgrades as code reviews
Platform teams that run many clusters use Cluster API so that a version number change in a reviewed file triggers a rolling replacement of machines. The same pull request process that applies to application code now applies to infrastructure upgrades.
Realistic scenarios
The next stories are teaching illustrations. They are not descriptions of the internal systems of the companies named.
A streaming company before a big premiere
A video service like Netflix does not want a cluster change in the days before a major release. The platform team therefore keeps an upgrade calendar with freeze windows around big events, and runs its upgrades in quieter periods, one region at a time, with the first region acting as the canary.
An online marketplace with many clusters
A company like Amazon or Uber runs many clusters across regions. It upgrades a small internal cluster first, then a low-traffic region, then the rest in waves. Automated checks stop the rollout if error rates rise, so a single bad version never reaches every region.
A small team with one cluster
A startup with one production cluster on a managed service uses a staging cluster created from the same code. They upgrade staging on a Tuesday, let it run for a week, then upgrade production on the following Tuesday morning with the full team online. Simple, boring and effective.
Building an Upgrade Program
The goal is not to survive one upgrade. It is to make upgrades so routine that nobody dreads them.
A sample cadence
Let us imagine a team that follows a calm rhythm. The Kubernetes project releases a new minor version. The team waits a few weeks for early patches, since the first patch release often fixes early bugs, then begins its internal process.
Week 1: Read and scan
Read the release notes, list removed APIs and breaking changes, run scanners against all manifests and Helm releases, and open tickets for each fix. Check every add-on for compatible versions.
Week 2: Development cluster
Upgrade the development cluster using the same automation planned for production. Fix whatever breaks, and update the runbook.
Week 3: Staging cluster and soak
Upgrade staging, which mirrors production. Run load tests and the full smoke suite, and let it run for several days to find slow problems.
Week 4: Production in waves
Take an etcd snapshot, upgrade the control plane, then add-ons, then one canary node, then node batches. Keep an engineer watching dashboards, and keep the old node pool until the end of the day.
Week 5: Review
Hold a short review. What went well, what was slow, what surprised us? Update the runbook and automation, and retire old resources.
Automation is the multiplier
Every step above should be turned into automation over time: scanning for deprecated APIs in pipelines, checking add-on versions, creating snapshots, rolling node pools, running smoke tests and posting progress to a team channel. Automation does not replace judgment, but it removes tedious mistakes and makes the process repeatable by anyone on the team, not just the one person who remembers the steps.
Airlines do not rely on a pilot’s memory for pre-flight safety. They use checklists, practiced until automatic. A mature upgrade program is a checklist with tooling around it: the process holds the knowledge, and people use their attention for the unusual parts.
Measure your program
Track these three numbers over time. If lag keeps growing, you need more time or automation. If duration is very long, find the slowest step. If incidents appear, review which part of your preparation missed them. Over a few cycles, all three should trend in the right direction.
Upgrades work best when platform teams own the cluster and application teams own their applications’ readiness. Give application teams clear dates and a checklist for their side, such as removing deprecated APIs, setting budgets and handling shutdown signals.
Frequently Asked Questions
Quick answers to the questions that come up most often.
Not for the control plane in place. Upgrade one minor version at a time. If you want to leap further, a blue/green approach that builds a fresh cluster at the target version can do so, as long as your applications and data work with it.
Not if they are built for it. With a highly available control plane, multiple replicas, sensible disruption budgets and graceful shutdown, applications keep serving while nodes are drained and replaced. Single-replica applications will see a brief interruption.
They keep running. The kubelet on each node continues to manage existing containers. What pauses is change: scheduling, scaling and replacing failed pods.
Always the control plane. Nodes must never run a newer kubelet than the API server.
The skew policy allows kubelets to be several minor versions older than the API server, but it is best to upgrade nodes soon after the control plane. Check the current policy for the exact limit.
Not in the usual sense. Your recovery options are fixing forward, restoring from an etcd snapshot, rolling nodes or add-ons back, or moving traffic to an older cluster in a blue/green setup.
Use the API server’s deprecated request metrics and warnings, and scan manifests and Helm releases with tools such as Pluto or kube-no-trouble. Add schema validation to your pipelines.
Yes. Each add-on supports specific Kubernetes versions. Check the compatibility table for every one, and follow its upgrade guide for the proper order.
It is a good default for patch updates and for non-production clusters. For production, many teams use auto-upgrade with maintenance windows, an early-channel staging cluster, and the ability to pause around busy events.
It is safer to roll back, but it costs more and needs your platform to be fully reproducible from code. Many teams use in-place or surge upgrades for most clusters and reserve blue/green for the most critical ones.
A common rhythm is one minor upgrade per quarter or so, plus prompt patch upgrades. This keeps you well inside the support window without constant disruption.
Practice them often on a realistic staging cluster, with automation and a written runbook. Familiarity removes most of the risk.
Summary and Key Takeaways
The shortest possible version of everything above.
A Kubernetes cluster upgrade is a carefully ordered replacement of many cooperating parts, performed while applications keep running. The rules are not mysterious: version numbers tell you the risk, the skew policy tells you the order, deprecation notices tell you what to fix first, and disruption budgets and graceful shutdown tell you whether your applications can survive the move. With backups, small steps, staged environments and good dashboards, even large upgrades become uneventful.
Key Takeaways
- Upgrade often and in small steps. Move one minor version at a time, on a regular cadence, to stay within the supported window and keep each change small.
- Follow the order. Back up etcd, upgrade the control plane first, then add-ons, then nodes, because components must never be newer than the API server.
- Hunt deprecated APIs early. Scan manifests, Helm releases and live clusters, and fix them while the old versions still work.
- Make workloads upgrade-friendly. Use multiple replicas, sensible disruption budgets, accurate readiness checks, topology spread and graceful shutdown.
- Choose the right node strategy. In-place, rolling with surge, or blue/green node pools each trade cost, speed and safety, and managed services or Cluster API can automate them.
- Plan the way back before you begin. Downgrades are not generally supported, so rely on fresh snapshots, retained old node pools and practiced restores.
- Validate and learn. Watch dashboards, run smoke tests, record what happened, and improve the runbook and automation after every upgrade.