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..
Get the next one before it is old news
Independent analysis of cloud-native infrastructure, Kubernetes and data centre economics. No vendor spin.

[…] reading. Why containerd and CRI-O both won the runtime war, what auditors actually ask for in a supply chain review, and how the container management market […]
[…] you run Kubernetes at fleet scale, the runtime question is settled and containerd and CRI-O both won. The open question is fleet management, which is where the operating cost actually […]