Docker Demystified - Part 5: containerd vs CRI-O — Comparing High-Level Runtimes

containerd vs CRI-O

Both are high-level runtimes. Both can run containers for Kubernetes. The difference is philosophy.


containerd: Swiss Army Knife

Built for everyone — Docker, Kubernetes, standalone use, embedded systems.

containerd features:
├── Full image management
├── Content-addressable storage
├── Multiple snapshot drivers (overlayfs, btrfs, zfs)
├── Plugin architecture
├── Namespace isolation (multi-tenant)
├── Works without Kubernetes
└── gRPC API for custom integrations

History

  • 2017: Docker extracted containerd from their monolithic codebase
  • 2017: Donated to Cloud Native Computing Foundation (CNCF)
  • 2019: Graduated project status (mature, production-ready)
  • Today: Used by Docker, Kubernetes, AWS, Google, Azure

Key Characteristics

  • General-purpose - Can be used standalone or with any orchestrator
  • Feature-rich - Comprehensive image and storage management
  • Extensible - Plugin system for custom functionality
  • Widely adopted - Industry standard

CRI-O: Kubernetes-Only

Built by Red Hat specifically for Kubernetes. Does nothing more than what K8s needs.

CRI-O features:
├── CRI (Container Runtime Interface) implementation
├── Image pulling
├── Container lifecycle
└── That's it — if K8s doesn't need it, CRI-O doesn't have it

Motto: "Boring is good" — minimal surface area, fewer bugs

History

  • 2016: Red Hat started CRI-O project
  • 2017: First stable release aligned with Kubernetes v1.7
  • Today: Default runtime in OpenShift (Red Hat’s Kubernetes distribution)

Key Characteristics

  • Kubernetes-focused - Only implements CRI, nothing else
  • Minimal - Smaller codebase, fewer features
  • Stable - Less code = fewer bugs
  • OpenShift default - Tight integration with Red Hat ecosystem

Architecture Comparison

                        KUBERNETES
                            │
                       CRI (gRPC API)
                            │
               ┌────────────┴────────────┐
               ▼                         ▼
   ┌───────────────────┐     ┌───────────────────┐
   │    containerd     │     │      CRI-O        │
   │                   │     │                   │
   │ • Image management│     │ • Image management│
   │ • Snapshots       │     │ • Minimal features│
   │ • Content store   │     │ • K8s-focused only│
   │ • Multi-tenant    │     │                   │
   │ • Plugin system   │     │                   │
   └─────────┬─────────┘     └─────────┬─────────┘
             │                         │
             ▼                         ▼
   ┌───────────────────────────────────────────────┐
   │              OCI Runtime (runc)               │
   │         (or crun, youki, kata, gvisor)        │
   └───────────────────────────────────────────────┘

Both ultimately call the same low-level runtime. The high-level runtime is just the manager.


Feature Comparison

FeaturecontainerdCRI-O
Primary Use CaseGeneral-purposeKubernetes only
Image ManagementFull-featuredBasic, sufficient
Storage DriversMultiple (overlayfs, btrfs, zfs)overlayfs primary
Standalone UsageYes, via ctr or nerdctl CLINo
Plugin SystemYes, extensibleNo
Multi-tenancyNamespace isolationNot needed (K8s handles)
Default OCI Runtimerunccrun (can use runc)
APIgRPC (general)CRI gRPC only
Codebase SizeLargerSmaller
MaintenanceCNCF + communityRed Hat + community

Performance

Startup Time

CRI-O has slightly faster pod startup in some benchmarks:

  • Smaller codebase
  • Less overhead
  • Uses crun (faster than runc)

containerd is still very fast:

  • Difference is milliseconds in practice
  • Negligible for most workloads

Resource Usage

Both are lightweight:

Idle resource usage:
├── containerd: ~30-50 MB RAM
└── CRI-O: ~20-40 MB RAM

Difference is minimal on modern systems.

Who Uses What

PlatformHigh-Level RuntimeLow-Level Runtime
Docker Desktopcontainerdrunc
Kubernetes (default)containerdrunc
OpenShift (Red Hat)CRI-Ocrun
GKE (Google)containerdrunc
EKS (AWS)containerdrunc
AKS (Azure)containerdrunc

Everyone except Red Hat converged on containerd + runc.


Why CRI-O Prefers crun

CRI-O defaults to crun instead of runc. Here’s why:

Featurerunccrun
LanguageGoC
Startup timeGoodFaster
Memory usageHigherMuch lower
systemd integrationGoodExcellent
cgroup v2SupportedNative-first
Pod densityGoodBetter

For high-density Kubernetes nodes (many pods per node), crun’s lower memory footprint and faster startup add up. Red Hat optimizes for this in OpenShift.


When to Choose containerd

Use containerd if:

  1. Running Docker - containerd is Docker’s runtime
  2. Need standalone usage - Want to use ctr or nerdctl CLI
  3. Custom integrations - Building on top of container runtime
  4. Plugin ecosystem - Need extensibility
  5. Industry standard - Want widest compatibility

When to Choose CRI-O

Use CRI-O if:

  1. OpenShift - It’s the default and best integrated
  2. Minimalism - Want smallest possible runtime
  3. Kubernetes-only - Don’t need standalone features
  4. Red Hat ecosystem - Tight integration with RHEL
  5. Security-focused - Smaller codebase = smaller attack surface

The CRI (Container Runtime Interface)

The gRPC interface that Kubernetes (kubelet) uses to talk to container runtimes. Both containerd and CRI-O implement CRI — so switching is theoretically possible (drain node → swap runtime → uncordon), and your images remain compatible due to OCI compliance. However, in practice it’s rarely done mid-cluster due to different storage paths, logging configurations, and default settings between runtimes. Most teams pick one at cluster creation and stick with it.

Cloud Providers Lock You In: Managed Kubernetes (GKE, EKS, AKS) does not let you change the runtime — containerd is the only option. OpenShift locks you into CRI-O. Only with self-managed Kubernetes (kubeadm, bare-metal) do you have full control over runtime choice.


Key Takeaways

  1. containerd = general-purpose - Works with Docker, K8s, standalone
  2. CRI-O = Kubernetes-only - Minimal, focused implementation
  3. Both are production-ready - Used by major companies
  4. Performance is similar - Negligible differences in practice
  5. containerd is more popular - Used by all major cloud providers
  6. CRI-O is OpenShift default - Best for Red Hat ecosystem
  7. Both use same OCI runtimes - runc/crun underneath
  8. Choice doesn’t affect workloads - OCI compliance ensures compatibility

Comments