Installing and Configuring containerd as a Kubernetes Container Runtime

Page content

This post shows you how to install containerd as the container runtime for Kubernetes and configure it to use the systemd cgroup driver. The commands below were tested on Ubuntu Server 24.04 with containerd 2.2.1 from the Ubuntu updates repository. The exact Ubuntu package revision can change as updates are published. See the Kubernetes container runtime documentation for more about runtime requirements.

Prepare the host

Kubernetes expects swap to be disabled by default. Turn it off now and comment out any swap entries in /etc/fstab so it stays off after a reboot.

sudo swapoff -a
sudo sed -ri '/\sswap\s/ s/^([^#])/#\1/' /etc/fstab

Load the kernel modules used by containerd and configure them to load at boot.

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

Set the networking sysctls Kubernetes needs, then apply them without rebooting.

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

sudo sysctl --system

Install containerd

Install the distribution package. On a clean Ubuntu Server 24.04 host, this installed containerd 2.2.1 and runc 1.3.4.

sudo apt-get update
sudo apt-get install -y containerd
containerd --version

The version output from that install was:

containerd github.com/containerd/containerd/v2 2.2.1

The Ubuntu package revisions observed were containerd 2.2.1-0ubuntu1~24.04.4 and runc 1.3.4-0ubuntu1~24.04.1. Your revisions may differ as the updates repository changes.

Configure containerd for Kubernetes

Generate the default configuration file. Containerd 2.x uses configuration version 3 and the io.containerd.cri.v1.runtime plugin path. See the containerd CRI configuration guide for details.

sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml

The kubelet and containerd need to use the same cgroup driver. kubeadm configures the kubelet to use systemd, so set SystemdCgroup to true for the runc runtime in /etc/containerd/config.toml:

version = 3

[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
  SystemdCgroup = true

You can change the generated default with sed:

sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

After installing kubeadm, set containerd’s sandbox (pause) image to the image expected by that kubeadm version. This avoids a version mismatch between the runtime default and the image kubeadm uses.

PAUSE_IMAGE=$(kubeadm config images list --kubernetes-version "$(kubeadm version -o short)" | grep pause)
echo "$PAUSE_IMAGE"
grep -n "sandbox = " /etc/containerd/config.toml
sudo sed -i "s#sandbox = '.*'#sandbox = '$PAUSE_IMAGE'#" /etc/containerd/config.toml
grep -n "sandbox = " /etc/containerd/config.toml

Restart the service to load the updated configuration and verify it is running.

sudo systemctl restart containerd
sudo systemctl status containerd.service --no-pager

After installing kubeadm and cri-tools, configure crictl to talk to containerd. You can then continue with kubeadm init or kubeadm join.

sudo crictl config --set runtime-endpoint=unix:///run/containerd/containerd.sock --set image-endpoint=unix:///run/containerd/containerd.sock
sudo crictl version

That’s it. containerd is ready for Kubernetes to use as its container runtime.