Setting up Kubernetes with containerd

Complete step-by-step guide to set up a Kubernetes cluster using kubeadm with containerd as the container runtime. Learn installation, configuration, and validation for production-ready clusters.

Installation Configuration Cluster Setup
Overview: Kubernetes with containerd

Since Kubernetes v1.24, containerd is the recommended and default container runtime. This guide walks you through setting up a Kubernetes cluster using kubeadm with containerd as the runtime. You'll learn how to install and configure containerd, set up kubeadm, and initialize a production-ready cluster.

Using containerd with Kubernetes provides better performance, lower resource usage, and tighter integration compared to Docker. It's the runtime used by all major cloud providers (AWS EKS, Google GKE, Azure AKS) and is the most battle-tested runtime for Kubernetes.

This guide covers a single-node cluster setup. For multi-node clusters, repeat the node preparation steps on each worker node and use the kubeadm join command provided during initialization.
Prerequisites
  • Linux server: Ubuntu 20.04+, Debian 11+, CentOS 8+, or RHEL 8+
  • Minimum resources: 2 CPU cores, 2GB RAM, 20GB disk space
  • Network: Internet access for pulling images, open ports (6443, 10250, etc.)
  • User: Root or user with sudo privileges
  • Hostname: Unique hostname (set with `hostnamectl set-hostname`)
Quick check: Run `uname -r` to verify kernel version (4.18+ recommended) and `swapoff -a` to disable swap (required for Kubernetes).
Step 1: Install containerd

First, install containerd on all nodes (control plane and workers). Here are the installation steps for Ubuntu/Debian and CentOS/RHEL.

# Ubuntu/Debian sudo apt-get update sudo apt-get install -y containerd # CentOS/RHEL sudo yum install -y containerd # Create default configuration sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml # Enable and start containerd sudo systemctl enable containerd sudo systemctl start containerd # Verify installation sudo systemctl status containerd containerd --version
containerd must be installed and running on every node before Kubernetes installation.
Step 2: Configure containerd for Kubernetes

containerd needs specific configuration to work with Kubernetes. The key settings are: using systemd cgroups (for better resource management), setting the pause image (sandbox), and configuring the CRI plugin.

# Edit containerd config sudo nano /etc/containerd/config.toml # Key settings for Kubernetes: [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.k8s.io/pause:3.9" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true # Apply configuration sudo systemctl restart containerd
The SystemdCgroup setting is critical for proper resource management with cgroup v2. Without it, you may encounter issues with resource limits.
Step 3: Install kubeadm, kubelet, kubectl

Install the Kubernetes components on all nodes. kubeadm is the tool for cluster initialization. kubelet runs on each node and manages containers. kubectl is the command-line tool for interacting with the cluster.

# Ubuntu/Debian sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl # CentOS/RHEL cat <
Step 4: Initialize the Kubernetes Cluster

On the control plane node, initialize the cluster using kubeadm. This sets up the API server, scheduler, controller manager, and etcd.

# Initialize the cluster sudo kubeadm init --pod-network-cidr=10.244.0.0/16 # The output will show the join command for worker nodes: # kubeadm join :6443 --token --discovery-token-ca-cert-hash sha256: # Set up kubectl for your user mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # Verify cluster status kubectl get nodes kubectl get pods -n kube-system
The `--pod-network-cidr` must match the CIDR used by your CNI plugin. For Flannel, use 10.244.0.0/16. For Calico, use 192.168.0.0/16.
Step 5: Install CNI Plugin (Network Add-on)

A CNI (Container Network Interface) plugin is required for pod networking. Without it, pods won't be able to communicate with each other. Here are the most popular CNI plugins:

# Flannel (simple and popular) kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml # Calico (advanced networking policies) kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/calico.yaml # Weave Net kubectl apply -f https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s.yaml # Verify CNI is working kubectl get pods -n kube-system # All pods should be Running
The CNI plugin must be installed before pods can communicate. The cluster will have `NotReady` nodes until CNI is installed.
Step 6: Join Worker Nodes

On each worker node, run the kubeadm join command from the initialization output. This registers the node with the cluster and starts the kubelet.

# On worker nodes: sudo kubeadm join :6443 --token --discovery-token-ca-cert-hash sha256: # Check if join was successful (on control plane) kubectl get nodes # Should show all nodes with Ready status
If you lose the join token, you can generate a new one on the control plane: `kubeadm token create --print-join-command`.
Step 7: Verify the Cluster

After setup, verify everything is working correctly.

# Check node status kubectl get nodes # Check all system pods kubectl get pods -n kube-system # Deploy a test pod kubectl run test-pod --image=nginx:alpine --restart=Never # Verify the pod is running kubectl get pods # Test networking kubectl run busybox --image=busybox --rm -it --restart=Never -- nslookup kubernetes # Delete test pod kubectl delete pod test-pod
Troubleshooting Common Issues
# Issue: Nodes not ready (CNI not installed) # Solution: Install CNI plugin (see Step 5) # Issue: containerd not starting sudo journalctl -u containerd -f # Issue: kubelet not starting sudo journalctl -u kubelet -f # Issue: Token expired # Generate new token on control plane kubeadm token create --print-join-command # Issue: Port conflicts # Check if ports are already in use sudo netstat -tulpn | grep 6443 # Reset cluster (if needed) sudo kubeadm reset sudo rm -rf /etc/kubernetes/ sudo rm -rf ~/.kube/ sudo systemctl restart containerd
Frequently Asked Questions
Can I use Docker instead of containerd with Kubernetes?
Yes, but it requires the cri-dockerd adapter. Docker Engine is no longer natively supported. containerd is the recommended and default runtime for Kubernetes v1.24+.
What CNI plugin should I use?
Flannel is the simplest and most popular for beginners. Calico offers more advanced network policies. Weave Net is also a good option. Choose based on your networking requirements.
How do I upgrade the cluster?
Use `kubeadm upgrade` commands. First upgrade the control plane: `kubeadm upgrade plan` then `kubeadm upgrade apply v1.29.0`. Then upgrade kubelet on each node.
What is the pause image?
The pause container holds the network namespace for each pod. It's a minimal container that runs in every pod and enables network sharing between containers in the same pod.
Can I set up a multi-node cluster with this guide?
Yes! Steps 1-3 apply to all nodes. Initialize the control plane (Step 4), then join workers (Step 6). The process is identical for each node.
How do I enable kubectl autocompletion?
Run `source <(kubectl completion bash)` and add it to your ~/.bashrc. For zsh, use `source <(kubectl completion zsh)`.
Why is SystemdCgroup important?
SystemdCgroup ensures proper cgroup management with systemd. It's required for resource limits to work correctly with cgroup v2, which is the default on modern Linux distributions.
How do I access the Kubernetes dashboard?
Install the dashboard with `kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml`. Then run `kubectl proxy` and access `http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/`.
Previous: crictl Commands Next: containerd vs CRI-O

Setting up Kubernetes with containerd is the modern way to build Kubernetes clusters. Follow this guide to get a production-ready cluster up and running.