Photo by Fernando Hernandez on Unsplash. Source: https://unsplash.com/photos/filled-glass-jar-efzwcMRM6j4 (Unsplash License).

Executive Summary

The runtime argument ended without a winner, because both finalists got what they needed. Containerd and CRI-O sit in the same chair today, between Kubernetes and the kernel. Docker kept the developer workflow and lost the node. That split is settled plumbing now, not an architecture decision.

The endgame ran through dockershim. Kubernetes deprecated the shim in v1.20 and removed it in v1.24 during 2022, and images kept running on every CRI runtime. What broke were tools that talked to the Docker daemon directly on a node, a monitoring agent here, a CI script there. Teams that needed Docker kept it through cri-dockerd. containerd graduated inside CNCF in February 2019 and sits under every managed Kubernetes service worth naming. CRI-O graduated in July 2023 and ships as the runtime under Red Hat OpenShift, where a smaller attack surface is the point. Both move with each Kubernetes minor release now. The interesting work moved to sandboxed runtimes such as gVisor and Kata Containers, selected per workload with RuntimeClass. Watch the interface, not the vendor.

The containerd vs CRI-O question used to start arguments among platform engineers. It does not anymore. Both runtimes are CNCF Graduated projects, both are stable, and your cluster barely notices which one you picked.

That calm is worth understanding, because the fight was never really about container runtimes. It was about who got to sit between Kubernetes and the Linux kernel. Containerd and CRI-O both won that seat. Docker lost the default path and kept the developer workflow.

Docker Left the Node and Nothing Fell Over

The runtime fight looked existential in 2018. Docker shipped the most popular engine, CoreOS pushed a rival called rkt, and Kubernetes maintained a shim to keep Docker working. Every new runtime meant another special case inside kubelet, and the project eventually refused to play that game.

Kubernetes used to reach Docker through a translation layer called dockershim. The shim shipped inside kubelet and slowly became maintenance debt, so the project deprecated it in v1.20 and removed it in v1.24 during 2022. The release notes still make useful reading today.

The removal sounded worse than it was. Docker images kept running on every CRI runtime. What broke were tools that reached past Kubernetes and talked to the Docker daemon directly on a node. A monitoring agent here, a CI script there. Teams that truly needed Docker kept it through cri-dockerd.

Newer teams often assume the release broke something. It did not. It removed an internal layer that most operators never configured in the first place. If a node ran Docker Engine, you performed one migration and one kubelet restart.

The lesson stuck. The node belongs to kubelet now. If you want to inspect it, you talk to the runtime through the Container Runtime Interface, not through a client built for humans.

containerd and CRI-O Are Two Safe Defaults

containerd is the runtime most clusters already run. It graduated inside CNCF in February 2019 and now sits under every managed Kubernetes service worth naming, plus Docker Engine itself. With no strong reason to choose otherwise, this is the default you get.

CRI-O was built for Kubernetes alone. It has no image builder, no friendly command line, and little reason to exist outside a cluster. It graduated as a CNCF project in July 2023 and ships as the runtime under Red Hat OpenShift. A smaller attack surface is the whole point.

So the containerd vs CRI-O comparison comes down to context more than quality. On OpenShift, CRI-O is the tested path and containerd is not. On a managed service, containerd is what you get. Neither choice is risky in 2026.

The Choice Matters Less Now Than It Did in 2020

Kubelet speaks one interface. Images follow one specification. Any runtime that implements the CRI will run your workloads, and swapping it does not touch your manifests. That is the main reason the debate cooled off.

The boring version of this story is the accurate one. Runtime selection stopped being an architectural decision and became a packaging detail, like which init system a Linux distribution ships by default.

Underneath both runtimes sits a lower layer that does the real kernel work. Containerd and CRI-O hand the container off to an OCI runtime, almost always runc. That split explains why the two projects can both be right at the same time.

Both projects also move at the same cadence now, shipping alongside each Kubernetes minor release. That shared rhythm removed the last reason to expect a stale runtime layer under a fresh cluster.

The interesting work moved down and up instead. Sandboxed runtimes such as gVisor and Kata Containers use that same interface to give each Pod stronger isolation. You select them per workload with RuntimeClass rather than rebuilding every node.

One low-level skill still pays off. kubectl reports what the cluster believes, not what the node is doing. When a Pod hangs without a clear event, a runtime tool like crictl shows what is truly running on the machine.

If you look at a node directly, kubelet points at the runtime through a single flag. It names one Unix socket per node, and containerd and CRI-O simply answer on different paths. That is the entire difference an operator has to hold in their head.

That is the useful takeaway. The runtime war is settled, and the real winner is the interface itself. Pick the default your platform team supports, learn the one debugging tool, and spend your attention on the layer above.

Related reading. Root on One Kubernetes Node Is Every Identity on It. The Most-Used Storage on Kubernetes Is the One You Get for Free. Cloud Native Transformation Is an Operating Model Change, Not a Lift and Shift. AI Infrastructure Runs on Four Layers. Most Break Below the Model..

By Ivan Tarin

Ivan Tarin is a Principal Product Marketing Manager at SUSE, where he owns go-to-market strategy and positioning for a seven-product cloud-native portfolio spanning Kubernetes, virtualization, storage, security, and observability. A former full-stack developer who shipped production code for enterprise and public-sector clients including U.S. national laboratories, Ivan translates complex infrastructure and AI technology into messaging that lands with developers, platform teams, and enterprise buyers. He has presented at KubeCon, SUSECON, and AWS Developer Week, and is currently pursuing an MS in Artificial Intelligence at the University of Colorado Boulder.

2 thoughts on “The Container Runtime Wars Are Over. containerd and CRI-O Both Won.”

Leave a Reply

Your email address will not be published. Required fields are marked *

Get the next one before it is old news

Independent analysis of cloud-native infrastructure, Kubernetes and data centre economics. No vendor spin.