What Is a Container Runtime? containerd, CRI-O, and Docker Explained
A clear explanation of what a container runtime is, the difference between high-level and low-level runtimes, and why Kubernetes moved away from Docker to containerd and CRI-O.
When people first learn Kubernetes, they often assume Docker is running the containers inside the cluster. After all, you built your image with Docker. It makes sense that Docker would run it too.
But if you check a modern Kubernetes node, you will find Docker is not there. Instead you will see containerd or CRI-O. Let me explain what is actually going on.
What Is a Container Runtime?
A container runtime is the software responsible for actually running containers on a host. When Kubernetes decides it needs to start a pod, something has to handle the low-level work of pulling the image, setting up filesystem layers, creating Linux namespaces to isolate the process, setting up cgroups for resource limits, and starting the actual process.
That "something" is the container runtime.
High-Level vs Low-Level Runtimes
There are actually two layers:
High-level runtimes manage image pulling, image storage, and container lifecycle. They talk to Kubernetes via a standard API called the CRI (Container Runtime Interface). Examples: containerd, CRI-O.
Low-level runtimes do the actual Linux-level work: namespaces, cgroups, process execution. The standard here is the OCI (Open Container Initiative) Runtime Specification. Examples: runc, crun, gVisor, Kata Containers.
In a typical Kubernetes setup:
Kubernetes (kubelet)
containerd (high-level runtime, via CRI)
runc (low-level runtime, via OCI)
your container process
Why Kubernetes Dropped Docker
Docker was originally designed as a developer tool — a complete platform for building, shipping, and running containers. Kubernetes only needed one small piece: the ability to run containers. But to use Docker, Kubernetes had to use it through a compatibility layer called dockershim.
This created real problems:
- dockershim was maintained by the Kubernetes team, not Docker
- Docker added overhead and complexity that Kubernetes did not need
- Docker features like swarm and compose added unnecessary attack surface
In Kubernetes 1.24 (released May 2022), Docker support was removed entirely.
The good news: your Docker-built images still work perfectly. Docker images follow the OCI image spec, which containerd and CRI-O both support. Nothing about your workflow changed.
containerd
containerd is a high-level container runtime originally extracted from Docker itself. Docker actually uses containerd internally. Today it is a standalone CNCF graduated project used by AWS EKS, GKE, and Azure AKS as the default runtime.
On a Kubernetes node, use crictl instead of docker:
# List running containers
crictl ps
# Pull an image
crictl pull nginx:latest
# Inspect a container
crictl inspect <container-id>CRI-O
CRI-O is a lightweight container runtime built specifically for Kubernetes. It implements exactly the CRI and nothing more. No extra features, no CLI for developers, no image building. Just what Kubernetes needs.
It is the default runtime for Red Hat OpenShift and is popular in security-focused environments because its minimal footprint means a smaller attack surface.
Specialized Low-Level Runtimes
The OCI spec means low-level runtimes are pluggable:
runc — The default. Fast, battle-tested, runs containers as regular Linux processes.
gVisor — Runs containers inside a user-space kernel. Stronger isolation, some performance overhead. Used for multi-tenant workloads where you do not fully trust container code.
Kata Containers — Runs each container inside a lightweight VM. Maximum isolation, more overhead.
In Kubernetes, you select the runtime via RuntimeClass:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
---
apiVersion: v1
kind: Pod
spec:
runtimeClassName: gvisor
containers:
- name: myapp
image: myapp:latestQuick Reference
| Runtime | Type | Used By | Key Feature |
|---|---|---|---|
| containerd | High-level | EKS, GKE, AKS | Default, battle-tested |
| CRI-O | High-level | OpenShift | Minimal, K8s-only |
| Docker | High-level | Developer workstations | Full-featured dev tool |
| runc | Low-level | Most clusters | Fast, standard |
| gVisor | Low-level | Security workloads | Kernel isolation |
| Kata Containers | Low-level | Maximum isolation | VM-level isolation |
For day-to-day work, the container runtime is invisible. The important thing: when you SSH into a Kubernetes node, reach for crictl, not docker.
Want to understand Kubernetes internals better? Read our Kubernetes architecture explained guide.
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Kind vs K3d vs Minikube — Best Local Kubernetes in 2026
Three tools for running Kubernetes on your laptop. They're not all equal — kind is fastest for CI, k3d is lightest for dev, minikube has the best feature set. Here's the full comparison.
What Are Linux cgroups and Namespaces? The Foundation of Containers Explained
Docker and Kubernetes containers are built on Linux cgroups and namespaces. Understanding these fundamentals helps you debug container issues and set resource limits properly.
What is OCI? The Container Image Standard Explained Simply
Learn what OCI (Open Container Initiative) is, what it standardizes, and why it matters for DevOps engineers. Covers image format, runtime spec, distribution spec, and practical tools like skopeo.