Understanding CRI-O: The Default OpenShift Container Runtime

CRI-O is the default container runtime in OpenShift Container Platform (OCP). It is a lightweight, Kubernetes-native runtime designed specifically to run containers according to the Open Container Initiative (OCI) standards. Unlike Docker, CRI-O is not a full container platform—it only provides the functionality Kubernetes needs to start, stop, and manage containers.

For OpenShift interviews, CRI-O is one of the most important platform components to understand.


Where CRI-O Fits in OpenShift

                    User
                      │
                  oc CLI / API
                      │
               kube-apiserver
                      │
                 Scheduler
                      │
                 kubelet
                      │
                CRI (gRPC API)
                      │
                  CRI-O
          ┌───────────┴────────────┐
          │                        │
     runc / crun             Image Management
          │                        │
          └───────────┬────────────┘
                      │
               Linux Kernel
          (Namespaces + cgroups + SELinux)

Key point: Kubernetes never talks directly to containers—it communicates with CRI-O through the Container Runtime Interface (CRI).


Why Red Hat Uses CRI-O Instead of Docker

Originally Kubernetes supported Docker through Dockershim.

Kubernetes
Dockershim
Docker

Problems:

  • Docker included many features Kubernetes didn’t need.
  • Additional translation layer (Dockershim).
  • Higher resource consumption.
  • Larger attack surface.
  • More maintenance.

When Dockershim was removed from Kubernetes, OpenShift adopted CRI-O because it is:

  • Kubernetes-native
  • OCI-compliant
  • Lightweight
  • Easier to secure
  • Easier to maintain

OpenShift Node Components

Every worker node typically runs:

Worker Node
RHCOS
├── kubelet
├── CRI-O
├── Machine Config Daemon
├── Network (OVN)
├── Node Exporter
└── Monitoring Agents

CRI-O is the service responsible for running all containers on the node.


Container Startup Flow

Suppose you create a Deployment:

oc create deployment nginx --image=nginx

The sequence is:

Deployment
ReplicaSet
Pod
Scheduler
Worker Node
kubelet
CRI-O
runc/crun
Linux Kernel
Container Running

What Happens Internally

Imagine the pod is scheduled to Worker-1.

Step 1

Scheduler assigns:

Pod
Worker-1

Step 2

kubelet notices:

Desired Pod
Need container

Step 3

kubelet calls CRI-O:

CreateContainer()
StartContainer()

through the CRI gRPC API.


Step 4

CRI-O:

  • pulls the image
  • creates filesystem
  • prepares networking
  • mounts volumes
  • creates namespaces
  • configures cgroups
  • applies SELinux labels

Step 5

CRI-O launches:

runc
or
crun

Step 6

The OCI runtime asks the Linux kernel to create:

  • PID namespace
  • Network namespace
  • Mount namespace
  • User namespace (if configured)
  • IPC namespace
  • cgroups

The application process then starts.


Components of CRI-O

CRI-O
├── CRI Server
├── Image Manager
├── Runtime Manager
├── Storage Manager
├── Networking
├── Logging
└── OCI Runtime

1. CRI Server

Receives requests from kubelet.

Examples:

RunPodSandbox()
CreateContainer()
StartContainer()
StopContainer()
RemoveContainer()

2. Image Manager

Responsible for:

Pull Image
Verify Image
Store Image
Reuse Cached Image

Uses:

  • Quay
  • Internal registry
  • Docker Hub
  • Other OCI registries

3. Storage

Uses the Linux OverlayFS storage driver.

Typical storage location:

/var/lib/containers/storage

Example:

Image Layers
OverlayFS
Writable Layer
Container

4. OCI Runtime

CRI-O does not execute containers directly.

It launches:

runc
or
crun

These create the container using Linux kernel primitives.


Why OpenShift Prefers crun

Recent OpenShift versions prefer crun because it offers:

  • Faster startup
  • Lower memory usage
  • Better cgroup v2 support
  • Better performance at scale

Networking

CRI-O does not configure networking itself.

It requests networking from Kubernetes.

CRI-O
CNI Plugin
OVN-Kubernetes
Pod IP
Network Ready

Storage

When a pod uses a PVC:

PVC
CSI Driver
StorageClass
Volume
CRI-O Mount
Container

CRI-O mounts the volume before starting the container.


Security

One reason Red Hat chose CRI-O is security.

Every container starts with:

  • SELinux labels
  • cgroups
  • namespaces
  • seccomp profiles
  • SCC restrictions
  • capabilities dropped
  • read-only root filesystem (when configured)

Example:

Container
SELinux
cgroups
Namespaces
Kernel

Logging

CRI-O captures stdout/stderr from containers.

Logs are typically stored under:

/var/log/containers/

These are then collected by logging agents such as Vector or Fluentd.

Flow:

Application
stdout
CRI-O
Node Log
Logging Stack

Image Pull

When an image is not cached:

Pod
CRI-O
Registry
Download
Verify
Store
Start Container

If cached:

Pod
CRI-O
Cached Image
Container Starts

CRI-O vs Docker

FeatureDockerCRI-O
Kubernetes optimizedNoYes
OCI compliantYesYes
Requires DockershimYes (historically)No
LightweightNoYes
Container runtime onlyNoYes
Image build capabilityYesNo
Native Kubernetes runtimeNoYes
Default in OpenShiftNoYes

Useful Commands

Check the CRI-O service:

systemctl status crio

View logs:

journalctl -u crio

Check running containers:

crictl ps

List images:

crictl images

Inspect pods:

crictl pods

Inspect a container:

crictl inspect <container-id>

Runtime information:

crictl info

Troubleshooting CRI-O

Pod stuck in ContainerCreating

Check:

oc describe pod <pod>

Then inspect:

  • Image pull errors
  • Volume mount failures
  • Network setup failures
  • CRI-O logs
journalctl -u crio

ImagePullBackOff

Verify:

crictl images

Check:

  • Registry availability
  • ImagePullSecrets
  • DNS resolution
  • Authentication

CrashLoopBackOff

Check:

oc logs <pod>
oc describe pod

Then verify:

  • Application startup
  • Memory limits
  • Exit codes
  • Liveness/readiness probes

CRI-O service down
systemctl status crio

If stopped:

systemctl restart crio

Then verify:

systemctl is-active crio

CRI-O and OpenShift Operators

The Machine Config Operator (MCO) manages CRI-O configuration across the cluster.

Example workflow:

MachineConfig
Machine Config Operator
Machine Config Daemon
Update /etc/crio/
Restart CRI-O (if required)
Node Ready

This ensures every worker node has a consistent runtime configuration.


CRI-O Integration with Other OpenShift Components

               OpenShift Cluster

                    API Server
                        │
                    Scheduler
                        │
                     kubelet
                        │
                     CRI-O
        ┌───────────────┼────────────────┐
        │               │                │
     Image Pull     Storage Mount     Networking
        │               │                │
     Registry         CSI Driver      OVN-Kubernetes
        │               │                │
        └───────────────┼────────────────┘
                        │
                    OCI Runtime
                  (crun / runc)
                        │
                  Linux Kernel

Interview Answer (2-Minute Version)

CRI-O is OpenShift’s default container runtime and implements the Kubernetes Container Runtime Interface (CRI). Instead of Kubernetes talking directly to Docker, the kubelet communicates with CRI-O over gRPC. CRI-O is responsible for pulling OCI-compliant images, preparing the container filesystem, mounting storage, configuring networking through the CNI plugins, applying security settings such as SELinux labels, cgroups, seccomp profiles, and Security Context Constraints, and finally launching the container using an OCI runtime such as crun or runc. Unlike Docker, CRI-O is purpose-built for Kubernetes, making it smaller, faster, and more secure. In OpenShift, its configuration is managed centrally by the Machine Config Operator, ensuring consistent runtime settings across all worker nodes. For troubleshooting, I typically start with oc describe pod, then check journalctl -u crio, use crictl ps, crictl images, and crictl inspect, and verify image pulls, storage mounts, networking, and runtime health.

Leave a Reply