Skip to main content
πŸŽ“ Claude Code Masterclass Learn AI-assisted development on Udemy β€” plus the companion book on Leanpub & Amazon. Start Learning
containerd vs CRI-O: Container Runtimes for Kubernetes
Platform Engineering

containerd vs CRI-O 2026: Kubernetes Runtime Comparison

containerd vs CRI-O compared for Kubernetes in 2026. Architecture, performance benchmarks, security, image pull speed, and which runtime to choose for.

LB
Luca Berton
Β· 4 min read

Both containerd and CRI-O are production-grade Kubernetes container runtimes. Both are CNCF graduated projects. Both use runc as the default OCI runtime. The differences are in scope, architecture, and where they are used.

Architecture

containerd

containerd is a general-purpose container runtime. It started as the core runtime inside Docker and was extracted into a standalone project. It handles image pulling, container lifecycle, storage, and networking β€” and exposes these through both the CRI (for Kubernetes) and its own API (for standalone use).

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Kubernetes                 β”‚
β”‚           (kubelet via CRI)            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚             containerd                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ CRI  β”‚ β”‚Image β”‚ β”‚  Snapshotter   β”‚ β”‚
β”‚  β”‚Pluginβ”‚ β”‚Store β”‚ β”‚(overlayfs/zfs) β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚              runc / crun               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

CRI-O

CRI-O is a Kubernetes-only container runtime. It implements the CRI specification and nothing else. No standalone container API, no image building, no extra features. It was built by Red Hat specifically to be the minimal runtime for Kubernetes.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Kubernetes                 β”‚
β”‚           (kubelet via CRI)            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚               CRI-O                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ CRI  β”‚ β”‚Image β”‚ β”‚   Storage    β”‚  β”‚
β”‚  β”‚ Only β”‚ β”‚Store β”‚ β”‚  (overlay)   β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚              runc / crun               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The key difference: containerd has a full API for non-Kubernetes use cases. CRI-O does not β€” if kubelet is not calling it, CRI-O has no purpose.

Feature comparison

FeaturecontainerdCRI-O
ScopeGeneral-purpose container runtimeCRI-only (Kubernetes-focused)
Used byDocker, K3s, EKS, GKE, AKS, RKE2OpenShift, Kubernetes
CNCF statusGraduatedGraduated (via Kubernetes)
CLI toolsctr, nerdctl, crictlcrictl only
Image buildingVia BuildKit/nerdctlNot included (use Buildah/Kaniko)
Standalone containersYes (via API or nerdctl)No
Snapshotter pluginsoverlayfs, zfs, btrfs, devmapper, stargzoverlayfs
Lazy image pullingstargz-snapshotter, NYDUSNo native support
Sandbox runtimesgVisor, Kata Containers, FirecrackerKata Containers
Version alignmentIndependent releasesMatches Kubernetes versions
Configurationconfig.tomlcrio.conf
Default OCI runtimeruncrunc (crun optional)

Performance

Both runtimes call runc (or crun) to create containers. The performance differences are in overhead before runc is invoked:

Container startup time

# Benchmark: 100 pod creates via CRI
# Measured on identical 8-core nodes, same Kubernetes version
Metriccontainerd 2.0CRI-O 1.31
Pod startup (cold)~350 ms~310 ms
Pod startup (warm, image cached)~180 ms~160 ms
Memory usage (daemon)~45 MB~25 MB
Memory per container (overhead)~2 MB~1.5 MB
1000 containers total daemon mem~200 MB~120 MB

CRI-O has a smaller memory footprint because it has less code β€” no non-CRI APIs, no snapshotter plugins, no BuildKit integration. At scale (1000+ containers per node), this matters.

Image pull speed

ImagecontainerdCRI-O
nginx:alpine (10 MB)1.2s1.3s
python:3.12 (350 MB)8.5s9.1s
pytorch:2.4-cuda12 (8 GB)45s48s

Image pull speeds are roughly equivalent. containerd has a slight edge due to stargz lazy pulling support for large images.

Lazy image pulling (containerd advantage)

containerd supports stargz and NYDUS snapshotters that pull only the layers needed at container startup, not the entire image:

# containerd config for stargz snapshotter
[proxy_plugins.stargz]
  type = "snapshot"
  address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"

For large AI/ML images (8-20 GB), lazy pulling reduces startup from minutes to seconds. CRI-O does not have this feature.

Security

FeaturecontainerdCRI-O
Seccomp defaultYes (default profile)Yes (default profile)
SELinuxSupportedFull support (Red Hat focus)
AppArmorSupportedSupported
User namespacesSupportedSupported
Read-only rootfsSupportedSupported
Attack surfaceLarger (more code, more APIs)Smaller (minimal code)
Sandbox runtimesgVisor, Kata, FirecrackerKata

CRI-O has a smaller attack surface by design β€” fewer features means fewer potential vulnerabilities. containerd offers more sandbox runtime choices for defense-in-depth.

Where each runtime is used

containerd is the default for:

  • Amazon EKS β€” default and only supported runtime
  • Google GKE β€” default runtime
  • Azure AKS β€” default runtime
  • K3s / RKE2 β€” Rancher’s lightweight Kubernetes distributions
  • Docker β€” containerd runs inside Docker Engine
  • Kind β€” Kubernetes-in-Docker for local development

CRI-O is the default for:

  • Red Hat OpenShift β€” default and only supported runtime
  • Fedora CoreOS β€” the OS for OpenShift nodes
  • CentOS Stream β€” Red Hat ecosystem
  • Kubernetes the hard way β€” popular choice for manual cluster builds

Installation

containerd

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y containerd.io

# Generate default config
sudo containerd config default | sudo tee /etc/containerd/config.toml

# Enable SystemdCgroup (required for Kubernetes)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

sudo systemctl restart containerd
sudo systemctl enable containerd

CRI-O

# Set Kubernetes version
KUBERNETES_VERSION=v1.31
PROJECT_PATH=prerelease:/main

# Add repository
curl -fsSL https://pkgs.k8s.io/addons:/cri-o:/$PROJECT_PATH/deb/Release.key |
  sudo gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://pkgs.k8s.io/addons:/cri-o:/$PROJECT_PATH/deb/ /" |
  sudo tee /etc/apt/sources.list.d/cri-o.list

sudo apt-get update
sudo apt-get install -y cri-o

sudo systemctl start crio
sudo systemctl enable crio

Switching runtimes

From containerd to CRI-O

# Drain the node
kubectl drain node01 --ignore-daemonsets --delete-emptydir-data

# Stop containerd
sudo systemctl stop containerd

# Install and start CRI-O
sudo apt-get install -y cri-o
sudo systemctl start crio

# Update kubelet to use CRI-O
sudo sed -i 's|containerd.sock|crio.sock|' /var/lib/kubelet/kubeadm-flags.env
sudo systemctl restart kubelet

# Uncordon
kubectl uncordon node01

From CRI-O to containerd

Same process in reverse. The containers are recreated by Kubernetes β€” the runtime switch is transparent to workloads.

Decision guide

Use containerd when:

  • You are on any managed Kubernetes (EKS, GKE, AKS) β€” it is already your runtime
  • You need standalone container capabilities beyond Kubernetes
  • You want lazy image pulling for large AI/ML images
  • You want the widest sandbox runtime choices (gVisor, Kata, Firecracker)
  • You are using K3s, RKE2, or Docker-based workflows

Use CRI-O when:

  • You are on OpenShift β€” it is the only supported runtime
  • You want the smallest possible attack surface for your runtime
  • You are building bare-metal Kubernetes and want the minimal CRI implementation
  • You prefer version-aligned releases that match Kubernetes versions exactly
  • You are in a Red Hat ecosystem (RHEL, Fedora CoreOS)

The practical truth: For most teams, the runtime is already chosen by their platform. If you are on a managed cloud, you are using containerd. If you are on OpenShift, you are using CRI-O. Both work. Both are stable. The choice rarely needs to be agonized over.

Free 30-min AI & Cloud consultation

Book Now