Skip to main content

Command Palette

Search for a command to run...

Part 2: Installing and Configuring containerd for Kubernetes

Updated
9 min read
Part 2: Installing and Configuring containerd for Kubernetes

Why the container runtime matters

Kubernetes does not run containers directly.

It communicates with a container runtime using the Container Runtime Interface (CRI). That runtime is responsible for:

  • Pulling container images

  • Creating and managing containers

  • Managing container storage

  • Handling low-level isolation

If the runtime is misconfigured, Kubernetes will fail in strange and frustrating ways.

In this guide, we will use containerd as the runtime. It is lightweight, production-ready, and widely adopted in modern Kubernetes environments.

What we will do in this part

  • Install CNI plugins (required by Kubernetes networking)

  • Install containerd manually

  • Configure containerd correctly for Kubernetes

  • Enable systemd cgroup driver

  • Install runtime debugging tools (nerdctl and crictl)

  • Enable IPv4 forwarding

  • Verify everything before installing Kubernetes

Installing CNI plugins

Kubernetes expects CNI binaries to exist on the node before networking is initialized.

Full Command

mkdir cni_plugins && \
cd cni_plugins/ && \
wget https://github.com/containernetworking/plugins/releases/download/v1.9.0/cni-plugins-linux-amd64-v1.9.0.tgz \
&& tar -xzf cni-plugins-linux-amd64-v1.9.0.tgz \
&& mkdir -p /opt/cni/bin && cp * /opt/cni/bin/

Step-by-Step Breakdown


1. Create a Working Directory

mkdir cni_plugins

What it does:

Creates a directory named cni_plugins.

Why it matters:

This keeps the downloaded archive and extracted files organized and separate from system directories.


2. Change Into the Directory

cd cni_plugins/

What it does:

Moves into the newly created directory so subsequent commands operate inside it.


3. Download the CNI Plugins Archive

wget https://github.com/containernetworking/plugins/releases/download/v1.9.0/cni-plugins-linux-amd64-v1.9.0.tgz

What it does:

Downloads the official CNI plugin bundle from the GitHub release page.

What is included:

The archive contains compiled CNI plugin binaries such as:

  • bridge

  • host-local

  • loopback

  • macvlan

  • ipvlan

  • bandwidth

  • portmap

  • tuning

These binaries provide the fundamental networking capabilities required by Kubernetes.


4. Extract the Archive

tar -xzf cni-plugins-linux-amd64-v1.9.0.tgz

Flag explanation:

  • -x — extract files

  • -z — decompress gzip

  • -f — specify the archive file

This extracts the plugin binaries into the current directory.


5. Create the CNI Binary Directory

mkdir -p /opt/cni/bin

What it does:

Creates the directory where Kubernetes expects CNI plugin binaries to be located.

Why /opt/cni/bin:

By default, kubelet looks for CNI plugin executables in:

/opt/cni/bin

If this directory does not exist or does not contain the plugins, pod networking will fail.

The -p flag ensures:

  • Parent directories are created if missing

  • No error occurs if the directory already exists


6. Copy the Plugins into the CNI Directory

cp * /opt/cni/bin/

What it does:

Copies all extracted plugin binaries into /opt/cni/bin.

Why this is required:

When Kubernetes creates a Pod, the kubelet invokes the appropriate CNI plugin binary from this directory to:

  • Configure container networking

  • Assign IP addresses

  • Attach network interfaces

  • Apply routing rules

Without these binaries in the expected location, the CNI integration will not function.

Understanding the Command Chaining

&&

Ensures that each command runs only if the previous command succeeds.
This prevents partial or inconsistent installations.

\

Line continuation character used to improve readability across multiple lines.


What This Command Accomplishes

After execution:

  • The CNI plugin bundle is downloaded

  • The archive is extracted

  • The binaries are installed in /opt/cni/bin

  • Kubernetes can invoke these plugins for pod networking

Note that this installs only the CNI plugin binaries.
You still need:

  • A CNI configuration file in /etc/cni/net.d/ (will be created during cluster bootstrap)

  • A networking implementation such as Flannel, Calico, or Cilium (we will use Calico in this series)

Summary

Step

Command

Purpose

1

mkdir cni_plugins

Create workspace

2

cd cni_plugins

Enter workspace

3

wget ...

Download CNI plugin bundle

4

tar -xzf ...

Extract binaries

5

mkdir -p /opt/cni/bin

Create CNI binary directory

6

cp * /opt/cni/bin

Install plugin binaries

Installing containerd

Downloading containerd and runc

1. Download the containerd Release Archive

Containerd releases are published on the official GitHub repository.

wget https://github.com/containerd/containerd/releases/download/v2.0.5/containerd-2.0.5-linux-amd64.tar.gz

This downloads the precompiled containerd binaries for Linux (amd64 architecture).


2. Download the systemd Service File

The containerd.service file is included in the containerd release repository.

You can download it directly:

wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service

This file allows systemd to manage containerd as a service.


3. Download runc (OCI Runtime)

Containerd relies on runc to create and run containers.

Download the latest compatible release from the official repository:

wget https://github.com/opencontainers/runc/releases/download/v1.4.0/runc.amd64

After downloading, make it executable:

chmod +x runc.amd64

What You Should Have After These Steps

You should now have:

  • containerd-2.0.5-linux-amd64.tar.gz

  • containerd.service

  • runc.amd64

These files are required before proceeding with extraction and installation.

Installation steps:

Commands

tar Cxzvf /usr/local/ containerd-2.0.5-linux-amd64.tar.gz
cp containerd.service /usr/lib/systemd/system/
install -m 755 runc.amd64 /usr/local/sbin/runc
mkdir /etc/containerd
containerd config default > /etc/containerd/config.toml

Step-by-Step Breakdown:


1. Extract the containerd archive

tar Cxzvf /usr/local/ containerd-2.0.5-linux-amd64.tar.gz

What it does:

  • tar — archive utility

  • C — change to directory before performing operations

  • x — extract

  • z — decompress gzip

  • v — verbose output

  • f — specify archive file

Effect:

Extracts the containerd binaries directly into /usr/local/.

This typically places:

  • containerd

  • containerd-shim

  • ctr

into:

/usr/local/bin/

Why /usr/local/:

This is a standard location for locally installed binaries that are not managed by the OS package manager.


2. Install the systemd Service File

cp containerd.service /usr/lib/systemd/system/

What it does:

Copies the containerd.service unit file into the systemd service directory.

Why this is required:

Systemd uses unit files to manage services.

Placing the file in /usr/lib/systemd/system/ allows systemd to:

  • Start containerd at boot

  • Manage its lifecycle

  • Track logs and failures

⚠ After this step, you can manage containerd as below but DON'T run these commands yet:

systemctl daemon-reload
systemctl enable containerd
systemctl start containerd

3. Install runc

install -m 755 runc.amd64 /usr/local/sbin/runc

What it does:

  • Installs the runc binary into /usr/local/sbin/

  • Sets permissions to 755 (owner can write; others can read/execute)

What is runc:

runc is the low-level container runtime responsible for:

  • Creating containers

  • Applying namespaces

  • Applying cgroups

  • Starting container processes

containerd uses runc to actually run containers according to the OCI specification.

Why /usr/local/sbin:

sbin directories typically store system-level administrative binaries.


4. Create the containerd Configuration Directory

mkdir /etc/containerd

What it does:

Creates the directory where containerd configuration files are stored.

Why /etc/containerd:

/etc is the standard location for system configuration files.


5. Generate the Default Configuration File

containerd config default > /etc/containerd/config.toml

What it does:

  • Runs containerd config default

  • Outputs the default configuration

  • Redirects it into /etc/containerd/config.toml

Why this matters:

This creates a baseline configuration file that you can customize.

Common modifications for Kubernetes environments include:

  • Enabling SystemdCgroup = true

  • Configuring registry mirrors

  • Adjusting snapshotter settings


What These Steps Accomplish

After completing these steps:

  • containerd binaries are installed

  • systemd is configured to manage containerd

  • runc is installed as the OCI runtime

  • A default containerd configuration file exists

At this point, containerd can be started and used as the container runtime for Kubernetes.


Section Summary:

Step

Command

Purpose

1

tar Cxzvf ...

Extract containerd binaries

2

cp containerd.service ...

Install systemd unit file

3

install -m 755 runc.amd64 ...

Install OCI runtime

4

mkdir /etc/containerd

Create config directory

5

containerd config default > ...

Generate default config


Configuring containerd for Kubernetes

Edit the configuration:

vim /etc/containerd/config.toml

1. Change the data root

Find the below line which is usually in the first couple of lines:

root = '/var/lib/containerd'

Replace with:

root = '/home/containerd_data'

This ensures container runtime data is stored on the dedicated mount created in Part 1.


2. Configure pause image (if using private registry)

If you use a private registry, configure the sandbox image:

[plugins.'io.containerd.cri.v1.images'.pinned_images]
  sandbox = '<private_registry>/pause:3.10'

⭕If using public registry, this section can remain default.


3. Enable systemd cgroups (Critical)

Find the runtime section and ensure:

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

This is essential because:

  • kubelet uses systemd as cgroup driver

  • Mismatch causes kubelet startup failures

🟠 Kubelet may automatically configure this property later on cluster bootstrap, but we explicitly declare it to avoid unnecessary conflicts.

This is one of the most common mistakes in Kubernetes setups.

Starting containerd

Enable and start the service:

systemctl enable --now containerd
systemctl status containerd

Ensure:

  • Service is active (running)

  • No immediate errors

Installing nerdctl and crictl

These tools help you debug container runtime issues.

1. Download crictl

crictl is a CLI for interacting with Kubernetes CRI-compatible runtimes such as containerd.

wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.33.0/crictl-v1.33.0-linux-amd64.tar.gz

2. Download nerdctl

nerdctl is a Docker-compatible CLI for containerd.

wget https://github.com/containerd/nerdctl/releases/download/v2.2.1/nerdctl-2.2.1-linux-amd64.tar.gz

3. Extract binaries:

tar Cxzvf /usr/local/bin/ nerdctl-2.0.5-linux-amd64.tar.gz
tar Cxzvf /usr/local/bin crictl-v1.33.0-linux-amd64.tar.gz

Create configuration for crictl:

vim /etc/crictl.yaml

Add:

runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: true
pull-image-on-create: false

This ensures crictl communicates correctly with containerd.


Enabling IPv4 forwarding

Kubernetes requires packet forwarding to be enabled.

Edit:

vim /etc/sysctl.conf

Set:

net.ipv4.ip_forward = 1

Apply changes:

sysctl -p
systemctl daemon-reexec
systemctl daemon-reload

Final verification before installing Kubernetes

Run:

crictl info

You should see:

  • Runtime connected

  • containerd information displayed

Also verify:

systemctl status containerd

If containerd is running and crictl connects successfully, your runtime layer is ready.


What’s next

In Part 3, we will install Kubernetes components (kubeadm, kubelet, and kubectl) and prepare the node for cluster initialization.