Search nomadLab

containerd 1.7 End of Support: Migrating Kubernetes Nodes to 2.x

Security support for containerd 1.7 stops on 1 September 2026. Go to 2.3 rather than 2.0, convert your registry config before you touch the binary, and let the 1.7 deprecation warnings write your checklist.

Updated

Security-only support for the containerd 1.7 branch ends on 1 September 2026. After that there are no CVE backports for the runtime under every pod on your nodes.

If you are on managed Kubernetes, your provider is already handling most of this and has probably nudged you in the console. If you build your own nodes with kubeadm, kops, or a baked AMI, this one is yours. The jump from 1.7 to 2.x is not an apt upgrade. There are removed APIs, a renamed CNI key, and a registry config format whose migration path has a failure mode that takes the CRI plugin offline while the kubelet still reports Ready.

Dates and version numbers below came off containerd’s RELEASES.md and the GKE deprecation docs on 21 August 2026.

Pick 2.3, and know why the others are traps

The support table is the whole decision, and it is less forgiving than people assume.

Remaining support windows for containerd branches as of 21 August 2026. Version 2.1 is already end of life since 3 July 2026. Version 1.7 ends 1 September 2026, 2.2 ends 6 November 2026, 2.0 ends March 2027, and 2.3 runs until 30 April 2028. Support remaining, from 21 August 2026 today 2.1 EOL ended 3 Jul 2026 1.7 1 Sep 2026, 11 days 2.2 6 Nov 2026 2.0 Mar 2027 2.3 30 Apr 2028 2.0 and 1.7 are on extended support from two volunteers, scoped to GKE. 2.3 is on the committers. 1.7 to 2.3 is a tested, supported direct LTS upgrade. Same effort as 2.0, twenty more months of patches.
Only one branch here has real runway. Going to 2.0 costs the same migration work and buys about seven months.

containerd guarantees a tested direct upgrade between sequential LTS releases, so 1.7 to 2.3 is supported outright. Note the flip side in the same document: ordinary minor upgrades must be sequential. 2.0 to 2.1 is supported, 2.0 to 2.2 is not. Landing on 2.0 now means either stepping through minors later or waiting for another LTS hop.

The extended support on 1.7 and 2.0 is also narrower than it reads. Committer support for 1.7 ended on 10 March 2026. What runs to September is two maintainers, Samuel Karp and Chris Henzie, and RELEASES.md scopes it explicitly to Kubernetes 1.30, 1.31, and 1.32 on Google Kubernetes Engine, adding that changes may not be accepted if they are not needed for that usage. If you are on 1.7 outside that lane, you are arguably unsupported today.

One more argument that settles it. containerd’s Kubernetes compatibility matrix lists 1.36 as requiring containerd 2.3.0+ or 2.2.0+. The 1.7 branch is not listed at all. Staying on 1.7 does not just cost you patches, it caps which Kubernetes version you can run.

As of 21 August 2026 the current patches are 2.3.4 and 1.7.34.

What actually breaks

CRI v1alpha2 is gone. Deprecated in 1.7, absent from 2.0. Anything talking that API directly, an ancient kubelet or a node agent or a monitoring sidecar, fails hard rather than warning. Kubernetes moved to CRI v1 in the 1.26 era, so a current kubelet is fine. If you are dragging a very old Kubernetes version and jumping the runtime at once, fix the kubelet first.

The AUFS snapshotter is removed. Deprecated back in 1.5, gone in 2.0. Modern kernels default to overlayfs, so this only reaches genuinely old hosts.

Docker Schema 1 images are disabled by default. Worth stating precisely, because it gets reported as a removal: containerd’s 2.0 notes say pulling Schema 1 images is disabled by default, with full removal planned later. For most fleets the practical effect is the same, a pull that fails only when that one pod tries to schedule. Anything you have not rebuilt since roughly 2017 is suspect.

The registry config keys are deprecated, not yet removed. mirrors, configs, and auths under the CRI registry plugin all still work in 2.x and log a warning. The trap is not the deprecation. It is what happens during migration.

The registry config trap

The old inline style, [plugins."io.containerd.grpc.v1.cri".registry.mirrors] in config.toml, has been deprecated since 1.5. The replacement is config_path, pointing containerd at a directory of hosts.toml files, one per registry host.

When 2.x reads an old config that still has inline registry.mirrors, the migration can leave you with both config_path and mirrors set. The CRI config validation treats that as fatal, not as a warning. From internal/cri/config/config.go:

return warnings, errors.New("`mirrors` cannot be set when `config_path` is provided")

The CRI plugin then refuses to load. The node comes up, the kubelet reports Ready, and no container can be created, because the failure is in the containerd log rather than the kubelet’s. That is an unpleasant thing to diagnose under load.

The fix is to not let the migrator near your registry config. Convert mirrors to hosts.toml and config_path by hand while still on 1.7, confirm pulls work, and then change the binary. Nothing inline is left for the migration to trip over.

While you are in the file, the CNI bin_dir key became the bin_dirs array. Both are still accepted in current 2.x and bin_dir logs a deprecation warning, so this is a cleanup item rather than a breakage.

The pre-flight that makes the rest safe

containerd built a good on-ramp here and very few people use it. Recent 1.6 and 1.7 releases emit deprecation warnings for configuration that will not survive the jump. That is a migration checklist generated from your actual config.

crictl info | jq '.config.containerd'
sudo journalctl -u containerd | grep -i deprecat

RELEASES.md is direct about what this buys you: if no deprecation warnings show up, the upgrade should be smooth. So fix them on 1.7, restart, confirm they are gone, and only then bump the version. Fixing config on the version you already run is reversible. Fixing config and swapping the binary at once means bisecting two changes under pressure.

In order: collect deprecation warnings from every node pool, convert registry mirrors to config_path, confirm your kubelet speaks CRI v1, audit image references for Schema 1 stragglers, and check the snapshotter is overlayfs. Clear those five and the version bump is anticlimactic, which is the goal.

By environment

Self-managed nodes. Replace rather than mutate. Cordon, kubectl drain, bring up a node built on a 2.3 image, uncordon. Rollback is deleting the new node while the old pool is still there to absorb the pods. In-place package upgrades work, but rolling one back means reinstalling the old package on a live node.

GKE. Google enforces this for you. GKE pauses automatic upgrades to 1.33 on Linux node pools when it detects deprecated feature usage, and resumes after 14 days with no such usage, or 3 days if the only finding is deprecated CRI registry.configs properties. GKE’s 1.33 Linux nodes run containerd 2.0, which is where the Schema 1 and CRI v1alpha2 removals land. If you do nothing, your cluster sits on an old minor until you clean up.

EKS and AKS. Both ship containerd in the managed node image and move it as you move Kubernetes versions, so the runtime rides along with normal node group upgrades. Your work is the same pre-flight plus staying on a supported Kubernetes version.

Verifying you landed it

Ready means the kubelet is happy. It does not prove the runtime is wired correctly, which is exactly the failure mode above.

crictl version
crictl info | jq '.config.containerd.runtimes'
crictl pull your-registry.example.com/some/image:tag
crictl ps -a | grep -iv running

Three things to confirm on the first replaced node: pulls from your private registry succeed, custom runtime handlers still resolve if you use gVisor or Kata, and nothing is stuck in a container-create loop. Keep one cordoned 1.7 node pool alive through a full day of production traffic before you delete it.

Turn on the deprecation warnings this week, before planning anything else. The output tells you whether you have a five minute config fix or a real project, and with eleven days left on the clock that is the difference between choosing your maintenance window and having one chosen for you.

Keep reading