CRI-O Linux: Complete Guide to Lightweight Container Runtime for Kubernetes

August 26, 2025

CRI-O (Container Runtime Interface – Open Container Initiative) is a lightweight, OCI-compliant container runtime specifically designed for Kubernetes. Unlike Docker, CRI-O focuses solely on running containers for Kubernetes pods, making it more streamlined and efficient for container orchestration environments.

What is CRI-O?

CRI-O is an implementation of the Kubernetes Container Runtime Interface (CRI) that enables the use of OCI-compatible runtimes. It was developed by Red Hat as an alternative to Docker for Kubernetes deployments, offering better performance and security through its minimalist approach.

Key Features of CRI-O

  • OCI Compliance: Fully compatible with Open Container Initiative standards
  • Kubernetes Native: Built specifically for Kubernetes container orchestration
  • Lightweight: Minimal overhead compared to Docker daemon
  • Security-focused: Enhanced security features and isolation
  • Systemd Integration: Native integration with systemd for better process management

CRI-O vs Docker: Key Differences

Aspect CRI-O Docker
Primary Purpose Kubernetes-focused runtime General-purpose container platform
Resource Usage Lower memory footprint Higher resource consumption
Architecture Daemonless Daemon-based
Image Management Basic pull/push operations Comprehensive image management

Installing CRI-O on Linux

Prerequisites

Before installing CRI-O, ensure your system meets the following requirements:

  • Linux kernel version 3.10 or higher
  • systemd service manager
  • Container networking plugins (CNI)
  • Sufficient storage space for container images

Installation on Ubuntu/Debian

Follow these steps to install CRI-O on Ubuntu or Debian systems:

# Update package repository
sudo apt update

# Install required packages
sudo apt install -y software-properties-common curl

# Add CRI-O repository
export VERSION=1.28
echo "deb [signed-by=/usr/share/keyrings/libcontainers-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_22.04/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list

# Add repository key
curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/xUbuntu_22.04/Release.key | sudo gpg --dearmor -o /usr/share/keyrings/libcontainers-archive-keyring.gpg

# Install CRI-O
sudo apt update
sudo apt install -y cri-o cri-o-runc

Installation on RHEL/CentOS/Fedora

# Install CRI-O on RHEL/CentOS
sudo dnf install -y cri-o

# Or for older systems using yum
sudo yum install -y cri-o

# Enable and start CRI-O service
sudo systemctl enable crio
sudo systemctl start crio

Verifying Installation

After installation, verify that CRI-O is running correctly:

# Check CRI-O service status
sudo systemctl status crio

# Expected output:
● crio.service - Container Runtime Interface for OCI (CRI-O)
   Loaded: loaded (/usr/lib/systemd/system/crio.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2025-08-26 08:15:23 IST; 2min ago
     Docs: https://github.com/cri-o/cri-o
 Main PID: 12345 (crio)
   Tasks: 11
   Memory: 45.2M
   CGroup: /system.slice/crio.service
           └─12345 /usr/bin/crio

CRI-O Configuration

Main Configuration File

CRI-O’s primary configuration file is located at /etc/crio/crio.conf. This file contains all runtime settings and options.

# View current configuration
sudo cat /etc/crio/crio.conf

# Key configuration sections:
[crio]
  storage_driver = "overlay"
  storage_option = []
  log_level = "info"

[crio.api]
  listen = "/var/run/crio/crio.sock"
  stream_address = ""
  stream_port = "0"

[crio.runtime]
  runtime = "runc"
  runtime_untrusted_workload = ""
  default_workdir = "/var/lib/crio"

Network Configuration

Configure CNI plugins for container networking:

# Create CNI configuration directory
sudo mkdir -p /etc/cni/net.d

# Create a basic bridge network configuration
sudo tee /etc/cni/net.d/10-crio-bridge.conf << EOF
{
  "cniVersion": "0.4.0",
  "name": "crio-bridge",
  "type": "bridge",
  "bridge": "cni0",
  "isGateway": true,
  "ipMasq": true,
  "ipam": {
    "type": "host-local",
    "ranges": [
      [{"subnet": "10.88.0.0/16"}]
    ],
    "routes": [
      {"dst": "0.0.0.0/0"}
    ]
  }
}
EOF

Basic CRI-O Commands and Operations

Managing Container Images

CRI-O works with OCI-compatible images. Use these commands to manage images:

# Pull an image using crictl (CRI-O client)
sudo crictl pull nginx:latest

# List available images
sudo crictl images

# Expected output:
IMAGE               TAG                 IMAGE ID            SIZE
nginx               latest              5d0da3dc9764        142MB

# Remove an image
sudo crictl rmi nginx:latest

Working with Containers

# List running containers
sudo crictl ps

# List all containers (including stopped)
sudo crictl ps -a

# Get container logs
sudo crictl logs CONTAINER_ID

# Execute commands in running container
sudo crictl exec -it CONTAINER_ID /bin/bash

# Stop a container
sudo crictl stop CONTAINER_ID

# Remove a container
sudo crictl rm CONTAINER_ID

Creating and Running Containers

Pod Configuration Example

Create a pod configuration file for testing:

# Create pod configuration
cat << EOF > test-pod.json
{
  "metadata": {
    "name": "test-pod",
    "namespace": "default"
  },
  "logDirectory": "/var/log/pods",
  "linux": {
    "cgroupParent": "kubepods-besteffort.slice:kubepods-besteffort"
  }
}
EOF

# Run the pod
POD_ID=$(sudo crictl runp test-pod.json)
echo "Pod ID: $POD_ID"

Container Configuration Example

# Create container configuration
cat << EOF > nginx-container.json
{
  "metadata": {
    "name": "nginx-container"
  },
  "image": {
    "image": "nginx:latest"
  },
  "command": [
    "nginx",
    "-g",
    "daemon off;"
  ],
  "linux": {},
  "log_path": "nginx.log"
}
EOF

# Create and start the container
CONTAINER_ID=$(sudo crictl create $POD_ID nginx-container.json test-pod.json)
sudo crictl start $CONTAINER_ID

echo "Container ID: $CONTAINER_ID"

Monitoring and Troubleshooting

Viewing Container Statistics

# Get container statistics
sudo crictl stats

# Expected output:
CONTAINER           CPU %               MEM USAGE / LIMIT   MEM %               NET I/O
abc123def456        0.50                64MiB / 2GiB       3.2                 1.2kB / 0B

# Get detailed container information
sudo crictl inspect CONTAINER_ID

# View pod information
sudo crictl inspectp POD_ID

Log Management

# View CRI-O daemon logs
sudo journalctl -u crio -f

# View container logs with timestamps
sudo crictl logs --timestamps=true CONTAINER_ID

# Get last 50 lines of logs
sudo crictl logs --tail=50 CONTAINER_ID

# Follow container logs in real-time
sudo crictl logs -f CONTAINER_ID

Security Features in CRI-O

SELinux Integration

CRI-O provides excellent SELinux support for enhanced container security:

# Check SELinux status
sestatus

# View CRI-O SELinux contexts
sudo ps -eZ | grep crio

# Expected output:
system_u:system_r:container_runtime_t:s0 12345 ? 00:00:02 crio

Seccomp Profiles

Configure custom seccomp profiles for containers:

# Create custom seccomp profile
sudo mkdir -p /var/lib/crio/seccomp

cat << EOF | sudo tee /var/lib/crio/seccomp/custom.json
{
  "defaultAction": "SCMP_ACT_ALLOW",
  "syscalls": [
    {
      "names": ["chmod", "fchmod", "fchmodat"],
      "action": "SCMP_ACT_ERRNO"
    }
  ]
}
EOF

Performance Optimization

Storage Configuration

Optimize CRI-O storage for better performance:

# Edit CRI-O configuration
sudo nano /etc/crio/crio.conf

# Optimize storage settings
[crio.image]
  default_transport = "docker://"
  global_auth_file = ""
  pause_image = "k8s.gcr.io/pause:3.2"
  pause_image_auth_file = ""
  pause_command = "/pause"

[crio.network]
  network_dir = "/etc/cni/net.d/"
  plugin_dirs = ["/usr/libexec/cni", "/usr/lib/cni"]

Resource Limits

# Configure runtime limits
[crio.runtime]
  pids_limit = 1024
  log_size_max = 52428800
  container_exits_dir = "/var/run/crio/exits"
  container_attach_socket_dir = "/var/run/crio"

Integration with Kubernetes

Configuring kubelet for CRI-O

To use CRI-O with Kubernetes, configure kubelet appropriately:

# Edit kubelet configuration
sudo nano /etc/kubernetes/kubelet/kubelet-config.yaml

# Add CRI-O socket configuration
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
containerRuntimeEndpoint: "unix:///var/run/crio/crio.sock"
imageServiceEndpoint: "unix:///var/run/crio/crio.sock"
runtimeRequestTimeout: "10m"

# Restart kubelet service
sudo systemctl restart kubelet

Common Issues and Solutions

Permission Issues

# Fix CRI-O socket permissions
sudo chmod 660 /var/run/crio/crio.sock
sudo chown root:crio /var/run/crio/crio.sock

# Add user to crio group
sudo usermod -aG crio $USER
newgrp crio

Storage Space Issues

# Check storage usage
sudo du -sh /var/lib/containers/storage/

# Clean up unused images and containers
sudo crictl rmi --prune
sudo crictl rm --all --force

# Configure automatic cleanup
echo 'log_size_max = 52428800' | sudo tee -a /etc/crio/crio.conf

Best Practices for CRI-O

Security Best Practices

  • Enable SELinux: Always run CRI-O with SELinux enabled for better security
  • Use least privilege: Configure containers with minimal required permissions
  • Regular updates: Keep CRI-O and its dependencies updated
  • Network policies: Implement proper network segmentation and policies
  • Image scanning: Scan container images for vulnerabilities before deployment

Performance Best Practices

  • Storage optimization: Use appropriate storage drivers for your workload
  • Resource limits: Set appropriate CPU and memory limits for containers
  • Log rotation: Configure proper log rotation to prevent disk space issues
  • Monitoring: Implement comprehensive monitoring and alerting
  • Regular cleanup: Schedule regular cleanup of unused images and containers

Migration from Docker to CRI-O

When migrating from Docker to CRI-O, follow these steps:

# 1. Stop and disable Docker
sudo systemctl stop docker
sudo systemctl disable docker

# 2. Install and configure CRI-O
sudo apt install -y cri-o

# 3. Update Kubernetes configuration
sudo sed -i 's|/var/run/dockershim.sock|/var/run/crio/crio.sock|' /var/lib/kubelet/kubeadm-flags.env

# 4. Restart kubelet
sudo systemctl restart kubelet

# 5. Verify the migration
kubectl get nodes -o wide

Conclusion

CRI-O represents a significant advancement in container runtime technology, offering a lightweight, secure, and Kubernetes-native solution for container orchestration. Its focus on OCI compliance, reduced resource consumption, and enhanced security features make it an excellent choice for production Kubernetes deployments.

The streamlined architecture of CRI-O eliminates unnecessary complexity while providing all the essential features needed for container runtime operations. By following the installation steps, configuration guidelines, and best practices outlined in this guide, you can successfully deploy and manage CRI-O in your Linux environment.

As the container ecosystem continues to evolve, CRI-O stands out as a robust, efficient alternative to traditional container runtimes, particularly for organizations looking to optimize their Kubernetes infrastructure while maintaining security and performance standards.