containerd is a high-performance, industry-standard container runtime that implements the Open Container Initiative (OCI) specification. While Docker uses containerd internally as its core container lifecycle manager, containerd can also run standalone as a Kubernetes container runtime — this is the default runtime for most managed Kubernetes services (EKS, GKE, AKS) and the recommended runtime for kubeadm-based clusters. Compared to Docker-in-Kubernetes (which adds an unnecessary layer), containerd communicates directly with the Kubernetes Container Runtime Interface (CRI), reducing overhead and improving startup latency. On RHEL 9, containerd is available from Docker’s official package repository and is the required runtime for Kubernetes clusters following the deprecation of dockershim in Kubernetes 1.24. This guide covers installing and configuring containerd on RHEL 9 as a standalone container runtime and as the Kubernetes CRI.

Prerequisites

  • RHEL 9 with sudo/root access

Step 1 — Install containerd

# Install from Docker's repository (official source)
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
dnf install -y containerd.io

# Generate the default configuration
containerd config default > /etc/containerd/config.toml

systemctl enable --now containerd
systemctl status containerd

Step 2 — Configure for Kubernetes (SystemdCgroup)

# The default config uses cgroupfs — Kubernetes requires systemd cgroup driver
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

# Verify the change
grep 'SystemdCgroup' /etc/containerd/config.toml

systemctl restart containerd

Step 3 — Use containerd CLI (ctr and nerdctl)

# ctr is the low-level containerd CLI (ships with containerd)
ctr version

# Pull and run a container (uses 'default' namespace — not visible to Kubernetes)
ctr images pull docker.io/library/nginx:alpine
ctr run --rm docker.io/library/nginx:alpine nginx-test /bin/sh -c "nginx -v"

# Install nerdctl — a Docker-compatible CLI for containerd
curl -fsSL https://github.com/containerd/nerdctl/releases/latest/download/nerdctl-2.0.0-linux-amd64.tar.gz | tar xz -C /usr/local/bin/
nerdctl run --rm nginx:alpine nginx -v

Step 4 — Manage Kubernetes Container Images

# Kubernetes uses the 'k8s.io' namespace in containerd
# Use crictl to interact with the Kubernetes-managed runtime

# Install crictl
VERSION="v1.31.0"
curl -fsSL https://github.com/kubernetes-sigs/cri-tools/releases/download/${VERSION}/crictl-${VERSION}-linux-amd64.tar.gz | tar xz -C /usr/local/bin/

# Configure crictl to use containerd
cat > /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
EOF

# List running Kubernetes containers
crictl ps
crictl images

Step 5 — Configure Image Registry Mirrors

# Configure a private registry mirror in /etc/containerd/config.toml
# Find the [plugins."io.containerd.grpc.v1.cri".registry] section and add:

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
  endpoint = ["https://registry.example.com", "https://registry-1.docker.io"]

systemctl restart containerd

Conclusion

containerd on RHEL 9 is the recommended Kubernetes container runtime for both new installations and migrations from Docker. The critical configuration change — setting SystemdCgroup = true — is the single most common cause of Kubernetes node instability when using containerd, as mismatched cgroup drivers between containerd and kubelet cause pod scheduling failures and random container crashes. For day-to-day container management on containerd without Kubernetes, nerdctl provides a familiar Docker-compatible experience while crictl is the correct tool for inspecting Kubernetes-managed containers.

Next steps: How to Install Kubernetes with kubeadm on RHEL 9, How to Install Docker Engine on RHEL 9, and How to Build Multi-Stage Docker Images on RHEL 9.