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 NodeRHCOS│├── 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:
runcorcrun
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:
runcorcrun
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
| Feature | Docker | CRI-O |
|---|---|---|
| Kubernetes optimized | No | Yes |
| OCI compliant | Yes | Yes |
| Requires Dockershim | Yes (historically) | No |
| Lightweight | No | Yes |
| Container runtime only | No | Yes |
| Image build capability | Yes | No |
| Native Kubernetes runtime | No | Yes |
| Default in OpenShift | No | Yes |
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 checkjournalctl -u crio, usecrictl ps,crictl images, andcrictl inspect, and verify image pulls, storage mounts, networking, and runtime health.