Cloud Native 8 min read

How to Seamlessly Upgrade Kubernetes from Docker to Containerd

Learn a step‑by‑step process for migrating Kubernetes clusters (v1.24+) from the deprecated Docker runtime to the native Containerd CRI, covering compatibility checks, node preparation, installation, configuration, node draining, kubelet updates, validation, and common pitfalls such as cgroup driver mismatches.

Ray's Galactic Tech
Ray's Galactic Tech
Ray's Galactic Tech
How to Seamlessly Upgrade Kubernetes from Docker to Containerd

Background

Kubernetes 1.24+ deprecates the Docker shim; containerd is the native CRI‑compatible runtime. Replacing Docker removes the intermediate layer, making the control plane lighter and more efficient.

Key concepts

It is a container‑runtime replacement, not a Kubernetes version upgrade.

Docker is deprecated; Kubernetes no longer communicates via dockershim.

Containerd implements the CRI directly, allowing K8s to talk to it.

Docker itself builds on containerd, so the migration essentially removes the middle layer.

Upgrade procedure (test in a staging environment, then apply node‑by‑node)

Phase 1 – Preparation

Compatibility check

Ensure the Kubernetes version is compatible with Containerd (e.g., K8s 1.24+ with Containerd 1.6+).

Refer to the official version matrix for exact compatibility.

Backup

Back up persistent container data.

Important paths to back up:

/etc/containerd/config.toml
/var/lib/containerd
/etc/docker/daemon.json

Record current state

kubectl get pods -o wide | grep <node-name>
docker images
crictl images   # if crictl is installed

Install Containerd and CRI tools

Ubuntu/Debian:

sudo apt-get update
sudo apt-get install -y containerd cri-tools

CentOS/RHEL: sudo yum install -y containerd cri-tools Configure Containerd

Generate the default configuration and write it to /etc/containerd/config.toml:

sudo containerd config default | sudo tee /etc/containerd/config.toml

Set the cgroup driver to match the Kubelet (systemd):

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true

Optionally configure a private registry in the same way as Docker’s /etc/docker/daemon.json.

Load kernel modules

sudo modprobe overlay
sudo modprobe br_netfilter

Ensure the modules are loaded on boot (e.g., add them to /etc/modules-load.d/containerd.conf).

Phase 2 – Node switch

Drain the node

kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data

Stop Docker and the Kubelet

sudo systemctl stop kubelet
sudo systemctl stop docker
sudo systemctl disable docker

Enable and start Containerd

sudo systemctl enable containerd
sudo systemctl start containerd

Update the Kubelet configuration

The configuration file is usually /var/lib/kubelet/kubeadm-flags.env or a file passed via --config.

Set the runtime to remote and point to the Containerd socket:

--container-runtime=remote
--container-runtime-endpoint=unix:///run/containerd/containerd.sock

Remove any Docker‑specific flags.

Reload systemd and restart the Kubelet

sudo systemctl daemon-reload
sudo systemctl start kubelet

Validate the node

kubectl get nodes <node-name>
kubectl uncordon <node-name>

Repeat the above steps for each remaining node.

Phase 3 – Post‑migration validation & cleanup

Verify workloads

Check that networking, storage, and DNS are functional.

Ensure application pods restart correctly and become Ready.

Optional: uninstall Docker

Ubuntu/Debian:

sudo apt-get remove -y docker-ce docker-ce-cli containerd.io

CentOS/RHEL:

sudo yum remove -y docker-ce docker-ce-cli containerd.io

Removing Docker does not affect containers managed by Containerd, which are stored under /var/lib/containerd.

Learn the new debugging tools crictl ps, crictl pods, crictl images,

crictl logs <container-id>
ctr

for low‑level container operations.

Common issues and mitigations

Kubelet fails to start (cgroup driver mismatch) – Ensure SystemdCgroup = true in /etc/containerd/config.toml matches the Kubelet’s cgroup driver.

Images need to be re‑pulled – Docker and Containerd use different storage paths. Pre‑save images with docker save or push them to a private registry before migration.

Log path or format changes – Logs remain under /var/log/pods/ and /var/log/containers/. Adjust log collectors if they rely on Docker‑specific paths.

Monitoring metric changes – Update Prometheus or other scrapers to collect Containerd‑specific metrics or rely on Kubelet CRI metrics.

Network plugin compatibility – Most CNI plugins are runtime‑agnostic, but verify compatibility in the plugin’s documentation.

Debugging experience differs – Replace docker ps/logs with crictl ps/logs; use ctr for deeper inspection.

Security policies (AppArmor/SELinux) – Adjust policies for Containerd if required; usually they inherit host settings.

Conclusion

Successful migration hinges on thorough preparation, node‑by‑node execution, and correct cgroup configuration. After the switch, the cluster becomes lighter, more efficient, and aligns with Kubernetes’ future direction.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerKubernetesLinuxCluster MigrationcontainerdCRIcgroup
Ray's Galactic Tech
Written by

Ray's Galactic Tech

Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow together!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.