kubeadm on your own hosts, see Upgrade Kubernetes instead.
Overview
An EKS upgrade has four parts, and they are upgraded in this order:- The control plane. AWS runs it. You choose the version, and EKS upgrades it one minor version at a time.
- The nodes. Managed node groups are upgraded separately, after the control plane.
- The cluster add-ons. kube-proxy and CoreDNS have builds for each Kubernetes version and are updated after the nodes. The VPC CNI, the EBS CSI driver, and the Pod Identity agent support a range of versions.
-
Your tools and controllers.
kubectlon the machine you work from, and Helm-installed controllers such as the AWS Load Balancer Controller.
Version Rules
These rules decide what you upgrade, and when. They come from the Kubernetes version skew policy and the Amazon EKS upgrade documentation.Why Nodes Must Be Upgraded Every Third Minor Version
The kubelet on each node talks to the control plane’s API server. Kubernetes supports a kubelet that is up to three minor versions older than the API server. A node on 1.32 is therefore supported by control planes on 1.32, 1.33, 1.34, and 1.35, but not 1.36. So when you upgrade across several versions, you can upgrade the control plane up to three times while the nodes stay on their original version. Before the fourth control-plane upgrade, the nodes must be upgraded. In the 1.32 to 1.36 example, the nodes must reach at least 1.33 before the control plane moves to 1.36. Upgrading the nodes at every step, as AWS recommends, always satisfies this rule. Waiting up to three steps is allowed, and it reduces how many times the nodes are replaced.Before You Begin
Check Readiness
- Upgrade insights. In the EKS console, open the cluster and select the Upgrade insights tab. Resolve every item marked as an error for the target version.
-
Deprecated APIs. Scan the manifests and Helm releases in the cluster with a tool such as
plutoorkubent. Then read the EKS release notes for every version you will pass through. - Subnet capacity. The control-plane upgrade needs up to five free IP addresses in the cluster subnets.
- Third-party components. Every Helm-installed component must support each version you pass through, not only the final one. Check the support matrix of each component, for example Istio, External Secrets, the NVIDIA device plugin or GPU operator, and the AWS Load Balancer Controller. Upgrade any that do not support a version before the control plane reaches it.
Check How the Default Add-ons Are Installed
The VPC CNI, CoreDNS, and kube-proxy are installed in one of two ways:-
As EKS add-ons. They appear in
aws eks list-addons, and you update them withaws eks update-addon. -
Self-managed. A cluster created through the EKS API, the AWS CLI, or infrastructure as code without explicit add-on resources gets self-managed copies. They do not appear in
aws eks list-addons, and EKS never upgrades them.
vpc-cni, coredns, or kube-proxy is missing from the list, adopt it as an EKS add-on before you upgrade. First back up its current configuration. Adopting it overwrites custom settings such as environment variables on aws-node or edits to the CoreDNS Corefile.
--configuration-values.
Plan for Node Replacement
When you upgrade a managed node group, EKS launches replacement nodes before it drains and removes the old ones. During an upgrade the node group needs:- Spare capacity for at least one extra instance of each instance type, in the same availability zones.
- Enough instance quota. GPU instances count against the account’s On-Demand G and VT instance quota, which is measured in vCPUs. If a second GPU node does not fit within the quota, the update fails. See Upgrading When Capacity or Quota Is Limited.
-
Room to evict pods. Pod disruption budgets can block the drain. The
--forceflag on the node group update evicts the pods anyway.
Find Add-on Versions
AWS publishes new add-on builds regularly. Look up the current builds for your target version rather than copying versions from older notes:Add-on versions must be exact, such as
v1.36.0-eksbuild.21. A pattern such as v1.36.x is rejected.Upgrade kubectl
Install thekubectl version that matches the target Kubernetes version before each step. Because kubectl supports one minor version in either direction, the target version works against the control plane both before and after the upgrade.
linux/amd64 with linux/arm64, darwin/arm64, or darwin/amd64 as appropriate. On macOS, use shasum -a 256 -c instead of sha256sum --check.
If which -a kubectl lists another copy first, for example one bundled with a cloud SDK, put /usr/local/bin earlier in your PATH.
Upgrade One Version
This example upgrades from 1.35 to 1.36. To upgrade across several versions, see Upgrade Several Versions.Step 1: Prepare
- Upgrade kubectl to 1.36.
-
Confirm that every node runs the current version:
All nodes must be on 1.35 before the control plane moves to 1.36.
- Find the add-on versions for 1.36.
Step 2: Upgrade the Control Plane
Step 3: Upgrade the Node Groups
--kubernetes-version, each node group moves to the control plane’s version with the latest EKS-optimized AMI. A node group that uses a launch template with a custom AMI needs a new launch template version instead.
Step 4: Update kube-proxy and CoreDNS
PRESERVE keeps settings you changed on the add-on. If you want EKS to restore its defaults, use OVERWRITE.
Step 5: Update Other Add-ons and Controllers
- VPC CNI, EBS CSI driver, and Pod Identity agent. Update them if the version lookup shows a newer build, or if the installed build does not support the new version. Move the VPC CNI one minor version at a time.
- AWS Load Balancer Controller. See Update the AWS Load Balancer Controller.
- GPU components. Update the NVIDIA device plugin or GPU operator to a release that supports the new version.
Step 6: Verify
See Verify the Upgrade.Upgrade Several Versions
To go from 1.32 to 1.36, the control plane must pass through four upgrades: 1.33, 1.34, 1.35, and 1.36. You choose how the nodes keep up. The three approaches below trade the number of node replacements against the downtime you can accept.Option A: Upgrade Everything at Each Step
Repeat Upgrade One Version for each version in turn: 1.33, then 1.34, 1.35, and 1.36. At each step, upgradekubectl, the control plane, the node groups, kube-proxy, and CoreDNS.
This is the approach AWS recommends, and the cluster stays in a fully supported state throughout. The nodes are replaced four times, and each replacement needs spare capacity for the surge nodes.
Option B: Let the Nodes Fall Up to Three Versions Behind
Upgrade the control plane up to three times while the nodes stay on their current version, then upgrade the nodes. For 1.32 to 1.36:
The nodes are replaced twice instead of four times.
- Keep kube-proxy on the nodes’ build until the nodes are upgraded. kube-proxy may be up to three minor versions older than the control plane, never newer.
- Upgrade insights may warn about the version difference between the nodes and the control plane. Within three versions, the difference is supported.
- Steps 5 and 6 can run together. Upgrade the control plane, then the nodes.
Option C: Scale the Nodes to Zero
If the cluster can be offline for a maintenance window, scale every node group to zero, step the control plane through each version, and start the nodes once at the end. This is the fastest approach, and it needs no spare capacity for surge nodes, which helps when GPU capacity or quota is limited.-
Scale every node group to zero:
Record each node group’s current sizes first, so you can restore them.
-
For each version in turn, upgrade
kubectl, the control plane, and every node group:With no nodes, the node group upgrade only changes its configuration. It launches nothing and takes a minute or two. Because the node groups keep pace with the control plane, the nodes start on the final version when they come back. Repeat for 1.34, 1.35, and 1.36. -
Optional: test that nodes join at an intermediate version. If a version changes the node runtime, for example containerd 2 on EKS 1.34 AMIs, scale one node group to one node after that step. Confirm the node reaches
Ready, then scale it back to zero. While the node runs, you can also update kube-proxy to that version’s build. The kube-proxy build then stays within three versions of the final control plane when the nodes return. -
Start the nodes by restoring each node group’s original sizes:
- Update kube-proxy and CoreDNS to the final version’s builds, as in Step 4. Add-on updates need running nodes, so they wait until this point.
DEGRADED while the nodes are at zero, because their pods have nowhere to run. This clears when the nodes return.
In a cluster that spans several availability zones, an EBS volume can only attach to a node in its own zone. If fewer nodes come back than before, a pod whose volume is in another zone stays
Pending with a volume node affinity conflict. Record the zone of each persistent volume before you scale down, and make sure a node returns in each of those zones.Update the AWS Load Balancer Controller
The AWS Load Balancer Controller is installed with Helm and is not upgraded with the cluster. Check its releases for support of your target Kubernetes version.Starting with controller v3, the Helm chart version matches the controller version, for example chart
3.6.0 installs controller v3.6.0. For controller v2 releases, the chart version is 1.x, for example chart 1.13.4 installs controller v2.13.4.-
Find the available versions:
-
Update the CRDs.
helm upgradedoes not update CRDs, so apply them first: -
Update the IAM policy. New releases can need new permissions. Compare the controller’s IAM policy with the
docs/install/iam_policy.jsonfile at the release tag in the controller repository, and update the policy if it differs. -
Upgrade the release, keeping its current values:
If the controller is managed by Terraform with a
helm_releaseresource, change the chart version in your Terraform configuration and apply it instead of runninghelm upgrade. -
Verify:
Upgrading With Terraform
If the cluster is managed with Terraform, make the same changes through your configuration instead of the AWS CLI:- Change one minor version per apply. Set the cluster version variable to the next version, apply, verify, and then repeat.
-
Read every plan. An upgrade shows in-place changes (
~). If a plan shows a replacement (-/+) of the cluster, a node group, an IAM role, or a launch template, stop. A replacement usually means the wrong variable file or workspace, not an upgrade. - Know the order of node group changes. The AWS provider applies a node group’s version and launch template change before its size change. If a node group has running nodes and a pending version change, a scale-down in the same apply first tries to replace the nodes. To scale such a node group to zero, change its size with the AWS CLI first, then let Terraform apply the version.
-
Target the upgrade. If the configuration has unrelated changes waiting, limit the plan to the cluster, node group, and add-on resources with
-target, and save it with-outso thatapplyruns exactly what you reviewed. -
Do not change settings that force a new cluster. For example, in the AWS provider, changing
bootstrap_self_managed_addonson an existing cluster replaces it.
Verify the Upgrade
Roll Back
Within 7 days of an upgrade, EKS can roll back the control plane to the previous minor version. See Roll back a cluster to a previous Kubernetes version. After that window, the only way back is a new cluster on the earlier version. If you manage the cluster with Terraform, set the version variable back after a rollback, so that the next apply does not upgrade the cluster again.Troubleshooting
Node group update fails with InsufficientInstanceCapacity or AutoScalingGroupInvalidConfiguration
Node group update fails with InsufficientInstanceCapacity or AutoScalingGroupInvalidConfiguration
AWS had no free instance of that type in the availability zone for the replacement node. The update fails and the node group returns to
ACTIVE on its previous version.Retry later, reserve capacity first with an On-Demand Capacity Reservation, or use Option C, which launches no replacement nodes during the version steps.To test whether an instance type can start now, reserve one and cancel it at once:Node group update fails because of the instance quota
Node group update fails because of the instance quota
The replacement node would take the account past its instance quota. This is common with GPU instances. Check the quota:Request an increase, or upgrade with Option C.
Node group update fails with ResourceInUseException
Node group update fails with ResourceInUseException
Another update on the node group is still running. List the updates and wait for the latest one to finish before starting another:
An old node stays in Terminating:Wait
An old node stays in Terminating:Wait
Managed node groups use a lifecycle hook to drain a node before it terminates. If an update fails part way through, the hook can be left waiting. Release it:
Pods fail with ImagePullBackOff, or volumes fail to attach, right after nodes start
Pods fail with ImagePullBackOff, or volumes fail to attach, right after nodes start
New nodes pull every image at once. On a network with limited egress to Amazon ECR, pulls can take many minutes and time out at first. Until the
ebs-csi-node pod on a node is ready, EBS volumes cannot attach to that node, and pods report timed out waiting for external-attacher.Check the ebs-csi-node pod on the new node. If its images are still pulling, wait. VPC endpoints for ecr.api, ecr.dkr, and an S3 gateway endpoint make pulls faster and more reliable.Messages such as failed to sync secret cache: timed out waiting for the condition in the first minutes after a node joins are usually harmless and clear on retry.kube-proxy or CoreDNS updates fail or stay DEGRADED
kube-proxy or CoreDNS updates fail or stay DEGRADED
An add-on update needs running nodes to roll out. If the cluster has no nodes, the update cannot finish. Start the nodes first, then update the add-ons.If the update fails with a conflict, a field you changed differs from the add-on’s default. Use
--resolve-conflicts PRESERVE to keep your change, or OVERWRITE to replace it.Tips
Find Available GPU Instances in Your Region
An availability zone can offer an instance type and still have none free to start. Before an upgrade launches GPU nodes, check what each zone offers, how large each type is, whether it can start now, and how much quota the account has. GPU instance types each zone offers:Name=location,Values=<zone-a>,<zone-b> to --filters.
GPU count, GPU model, vCPUs, and memory of each type:
available means one instance of that type can start in that zone now. no capacity means AWS has none free at the moment. Capacity changes over time, so run the check again shortly before you start nodes.
The account’s GPU quota:
g6.12xlarge uses 48 vCPUs. A node group upgrade with running nodes needs room for the old and the replacement node at the same time.
Reserve GPU Capacity Before Starting Nodes
If GPU capacity is scarce in your zone, reserve it before you start or replace GPU nodes. Anopen reservation is used automatically by any matching instance, including one launched by a managed node group:
active, start or upgrade the node group. After the node is Ready, cancel the reservation. The running instance keeps running:
Test With a Smaller GPU Instance
To confirm that GPU nodes join the cluster on a new version when your usual GPU type has no capacity, start a smaller type that uses the same EKS-optimized NVIDIA AMI, such asg6.xlarge. A smaller type has fewer GPUs, so switch back before you run production workloads.
Change the instance type only while the node group has zero nodes. With nodes running, a launch template change replaces them, and the replacement needs capacity for the new type alongside the old one. The safe order is:
- Scale the node group to zero.
- Change the instance type.
- Scale up and test.
- Scale back to zero.
- Change the instance type back.
Work Safely With Terraform
-
Confirm the environment before every plan. If you keep several environments in one configuration, check that the backend and the variable file match the cluster you mean to change:
Running a plan with another environment’s variable file proposes replacing the cluster and everything attached to it.
-
Save plans and apply the saved file.
terraform plan -out=<file>followed byterraform apply <file>applies exactly what you reviewed. Note thatapplywith a saved plan does not ask for confirmation. -
Keep saved plans out of version control. Plan files can contain secret values in plain text. Write them to a dedicated directory, such as
plans/, and add it to.gitignore. -
Read the summary line.
Plan: X to add, Y to change, Z to destroy.An upgrade should show0 to destroy. Stop on any destroy or replacement you did not expect. -
A saved plan can go stale. If something changes between
planandapply, Terraform reportsSaved plan is stale. Run the plan again, review it, and apply the new file. -
Do not run an apply while an EKS update is in progress. Wait until the cluster and node groups are
ACTIVE. Otherwise the apply fails withResourceInUseException, or it starts a second update that repeats the failure that stopped the first one.
Allow Time for First Image Pulls
New nodes pull every container image they need at once. On a network with limited egress to Amazon ECR, the first start of a node can take 20 to 30 minutes, andImagePullBackOff events in that window are normal while pulls are retried. GPU nodes take the longest because their images are large. Allow for this in your maintenance window, and don’t treat a slow first start as a failure until the pulls have stopped retrying.
Download Tools Into a Temporary Directory
curl -O writes into the current directory and fails with Permission denied if you can’t write there. Download into a directory created with mktemp -d, as in Upgrade kubectl, so the commands work from any location.
