Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

DevOps · CNCF daily

CRI-O: A Lightweight Container Runtime Built Only for Kubernetes

CNCF Graduated. An OCI-based implementation of the Kubernetes Container Runtime Interface that runs pods through runc, crun or Kata Containers and nothing else.

7 min read

CNCF Graduated

  • CRI-O
  • Kubernetes
  • runc
  • conmon
  • OCI
CNCF project page

CRI-O is a container runtime whose only job is to run containers for the Kubernetes kubelet. It implements the Container Runtime Interface (CRI), pulls OCI images, starts containers through an OCI runtime such as runc, crun or Kata Containers, and reports their state back to Kubernetes. It addresses the need for a small, predictable runtime that follows Kubernetes releases closely and carries no features beyond what a node requires. CRI-O was started by Red Hat in the Kubernetes incubator in 2016, joined the CNCF in April 2019 and graduated on 19 July 2023.

CNCF daily · Sprint 2, day 5 · Container Runtime

At a glance

Category Runtime › Container Runtime
CNCF status Graduated. Accepted April 8, 2019; graduated July 19, 2023
Written in Go
License Apache 2.0
Scope Kubernetes CRI only; no general-purpose container CLI or build tooling
Versioning Each CRI-O minor version matches a Kubernetes minor version (for example CRI-O 1.31 with Kubernetes 1.31)
OCI runtimes runc, crun and Kata Containers, selected per pod through runtime classes

Architecture

flowchart TB
  KUBELET[kubelet] ==>|CRI gRPC| CRIO[CRI-O daemon]
  CRIO ==>|pull images| IMG[containers image<br/>library]
  IMG ==>|fetch layers| REG[Container registry]
  IMG ==>|unpack| STORE[containers storage<br/>layers on disk]
  CRIO ==>|create sandbox| NET[CNI plugins<br/>pod network]
  CRIO ==>|spawn per container| MON[conmon<br/>monitor process]
  MON ==>|OCI runtime spec| RT[runc or crun<br/>or Kata]
  RT ==>|namespaces and cgroups| CTR[Containers in the pod]
  MON -.->|exit status and logs| CRIO
  CRIO -.->|metrics and traces| OBS[Prometheus and OpenTelemetry]
  classDef actor fill:#eceff1,stroke:#455a64,stroke-width:1.5px,color:#1d1c1a
  classDef core fill:#0f766e,stroke:#064e4a,stroke-width:2px,color:#ffffff
  classDef ctrl fill:#ede7f6,stroke:#5e35b1,stroke-width:1.5px,color:#1d1c1a
  classDef data fill:#e3f2fd,stroke:#1565c0,stroke-width:1.5px,color:#1d1c1a
  classDef store fill:#fff3e0,stroke:#e65100,stroke-width:1.5px,color:#1d1c1a
  classDef obs fill:#e8f5e9,stroke:#2e7d32,stroke-width:1.5px,color:#1d1c1a
  class KUBELET ctrl
  class CRIO,MON,IMG core
  class RT,CTR,NET data
  class STORE,REG store
  class OBS obs

Key: teal blocks are the project itself, blue are workloads and runtimes, purple are control and configuration, amber are storage, green are observability, grey are external actors. Thick arrows show the primary path, dashed arrows telemetry and status.

Component Responsibility
CRI-O daemon Serves the CRI gRPC API to the kubelet, manages pod sandboxes and containers, and applies node-level configuration such as seccomp, SELinux and cgroup settings.
containers/image Pulls and verifies OCI and Docker images from registries, including signature policy and registry mirror rules.
containers/storage Stores image layers and container filesystems on the node, shared with tools such as Podman and Buildah.
conmon A small monitor process started for each container. It keeps the container’s stdio and exit code alive even if the CRI-O daemon restarts.
OCI runtime runc or crun creates the container from an OCI runtime spec; Kata Containers adds a lightweight virtual machine per pod.
CNI plugins Set up pod networking through the standard Container Network Interface when the sandbox is created.

Design principle. CRI-O does only what the kubelet asks for and relies on shared building blocks for everything else: the containers libraries for images and storage, conmon for supervision, and OCI runtimes for execution. Keeping the scope this narrow reduces the code that runs on every node and keeps its release cycle aligned with Kubernetes.

Production reference design

A typical deployment is a kubeadm, on-premises or OpenShift cluster where the node image is built with CRI-O as its runtime.

  1. Match versions. Install the CRI-O minor release that matches the cluster’s Kubernetes minor version, from the distribution’s CRI-O package repository.
  2. Install and start the service on each node (shown for a Debian-based node with the CRI-O repository already configured):
    sudo apt-get install -y cri-o
    sudo systemctl enable --now crio
    sudo crictl --runtime-endpoint unix:///var/run/crio/crio.sock info
  3. Point the kubelet at CRI-O by setting --container-runtime-endpoint=unix:///var/run/crio/crio.sock, or pass criSocket: unix:///var/run/crio/crio.sock to kubeadm init and kubeadm join.
  4. Configure the runtime in a drop-in file so node upgrades do not overwrite it:
    # /etc/crio/crio.conf.d/10-crio.conf
    [crio.runtime]
    default_runtime = "crun"
    cgroup_manager = "systemd"
    
    [crio.runtime.runtimes.crun]
    runtime_path = "/usr/bin/crun"
    
    [crio.runtime.runtimes.kata]
    runtime_path = "/usr/bin/kata-runtime"
    runtime_type = "vm"
  5. Expose stronger isolation selectively with a RuntimeClass, so only chosen workloads pay the virtual machine cost:
    apiVersion: node.k8s.io/v1
    kind: RuntimeClass
    metadata: { name: kata }
    handler: kata
    Pods then set runtimeClassName: kata.

Typical uses include OpenShift and other enterprise distributions, security-sensitive clusters that mix runc and Kata Containers, and teams that want the runtime version tied directly to the Kubernetes version they operate.

Operational considerations

  • Version coupling. Upgrade CRI-O together with the kubelet on each node, keeping the same minor version. Running a CRI-O release far from the cluster version is unsupported and can break newer CRI features.
  • Cgroup driver. The kubelet and CRI-O must use the same cgroup manager, normally systemd. A mismatch is a common cause of pods that start and then fail.
  • Daemon restarts. Because conmon supervises each container, restarting the crio service does not stop running containers. Plan restarts as part of node maintenance anyway, and drain nodes for upgrades.
  • Image policy and mirrors. Registry mirrors, signature verification and pull policy are set in the containers configuration under /etc/containers. Test them before enforcing, because a strict policy blocks pulls.
  • Debugging. Use crictl to inspect pods, containers and images on a node, since the Docker CLI does not apply. Journal logs for the crio unit are the first place to look for sandbox failures.

Adoption

  • The project’s ADOPTERS.md lists Red Hat OpenShift Container Platform, which uses CRI-O as its only supported runtime from OpenShift 4, and SUSE CaaS Platform, which made it the default in version 4.
  • It also lists Oracle Linux Cloud Native Environment, Lyft, Reddit, Adobe, Digital Science and HERE Technologies among production users, with Lyft reporting use since 2017 and Reddit since 2021 across its self-managed clusters.
  • The CNCF graduation announcement in July 2023 reported more than ten public adopters and cited IBM Cloud Satellite and Adobe among them. The list is described as non-exhaustive.

Alternatives

Solution Model Best suited to
containerd (CNCF Graduated) General-purpose runtime used by most managed Kubernetes services The common default; see the containerd brief
Docker Engine with cri-dockerd Docker daemon behind a CRI adapter Environments that still depend on Docker-specific tooling
Mirantis Container Runtime Commercial Docker-based engine Teams that need vendor support for a Docker lineage
Kata Containers or gVisor (as OCI runtimes) Sandboxed runtimes used beneath CRI-O or containerd Workloads that need stronger isolation than shared-kernel containers

Strengths and limitations

Strengths Limitations
Minimal scope: only the CRI, with no extra daemon features No general-purpose container CLI; node debugging uses crictl
Release versions track Kubernetes minors closely Must be upgraded in step with Kubernetes, which adds node upgrade planning
Supports runc, crun and Kata Containers per pod through runtime classes Smaller ecosystem and tutorial base than containerd
Default runtime on OpenShift, with enterprise vendor support Most managed cloud Kubernetes services default to containerd, so CRI-O is mostly a self-managed choice
Shares the containers libraries with Podman and Buildah Configuration is spread across crio and containers files, which needs clear ownership

Recommendation

Adopt when you run OpenShift, or a self-managed cluster where you control the node image and want a runtime that follows Kubernetes releases and supports mixed runc and Kata workloads. If you run a managed cloud service, use the runtime the provider supports, which is usually containerd. Standardize on one runtime per cluster and build the CRI-O version into your node upgrade runbook.

References: CNCF project page · CRI-O website · CRI-O repository · CRI-O adopters · CNCF graduation announcement · CNCF Landscape

Spotted something wrong or out of date?Suggest a correction. I review every suggestion before changing the page.