How to Set Up a Multi-Node Kubernetes Cluster with kubeadm

Step-by-step guide to creating a multi-node Kubernetes cluster using kubeadm and containerd on Ubuntu. Covers control plane setup, worker node joining, and CNI installation.

4 lessons · 9 min · Beginner

3 minutes reading time

Written by

Civo Team
Civo Team

Marketing Team at Civo

kubeadm is a tool that sets up a production-grade Kubernetes cluster on any Linux machines. It handles the control plane initialisation and generates the join commands for worker nodes. This guide walks through setting up a cluster with one control plane node and two worker nodes on Ubuntu.

This setup is useful for learning how Kubernetes works at a deeper level, or for running Kubernetes on bare metal or cloud VMs without a managed service.

What you will build

Three Ubuntu nodes:

  • control-plane — runs the API server, scheduler, controller manager, and etcd
  • node-1 — worker node
  • node-2 — worker node

Requirements

Each node needs:

  • Ubuntu 20.04 or 22.04
  • 2 CPUs or more
  • 2GB RAM or more
  • Full network connectivity between all nodes
  • Unique hostname, MAC address, and product_uuid per node
  • Root or sudo access
kubeadm_setup_sequence

Step 1: Run on all three nodes

The following steps must be completed on the control plane node and both worker nodes before initialising the cluster.

Load required kernel modules

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

Configure networking for Kubernetes

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

Install containerd

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

Create the containerd configuration file:

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

Restart containerd:

sudo systemctl restart containerd

Disable swap

Kubernetes requires swap to be disabled. The scheduler makes pod placement decisions based on available memory and swap can cause instability:

sudo swapoff -a

Install kubeadm, kubelet, and kubectl

Install the dependency packages:

sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl

Add the Kubernetes package repository signing key:

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

Add the Kubernetes repository:

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

Update the package list and install:

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl

Prevent these packages from being automatically updated:

sudo apt-mark hold kubelet kubeadm kubectl

Step 2: Initialise the control plane node

Run only on the control plane node:

sudo kubeadm init --pod-network-cidr=10.244.0.0/16

The --pod-network-cidr value must match the CNI plugin you plan to install. The value above is correct for Flannel.

When the command completes, set up kubectl access 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

Save the kubeadm join command from the output. You will need it in Step 4. It looks like this:

kubeadm join <control-plane-ip>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

Step 3: Install a CNI plugin

Without a CNI plugin, nodes will stay in NotReady state and pods will not be scheduled. Install Flannel on the control plane node:

kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

Verify the Flannel pods are running:

kubectl get pods -n kube-system

Wait until all pods show Running before proceeding.

Step 4: Join the worker nodes

Run the join command saved from Step 2 on each worker node:

sudo kubeadm join <control-plane-ip>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

Step 5: Verify the cluster is healthy

Run on the control plane node:

kubectl get nodes

Expected output once all nodes have joined and the CNI is running:

NAME STATUS ROLES AGE VERSION
control-plane Ready control-plane 5m v1.29.0
node-1 Ready <none> 2m v1.29.0
node-2 Ready <none> 1m v1.29.0

All three nodes should show Ready. If any node shows NotReady, check the CNI pods with kubectl get pods -n kube-system and check the kubelet logs with journalctl -u kubelet.

Frequently asked questions

Civo Team
Civo Team

Marketing Team at Civo

Civo is the Sovereign Cloud and AI platform designed to help developers and enterprises build without limits. We bridge the gap between the openness of the public cloud and the rigorous security of private environments, delivering full cloud parity across every deployment. As a team, we are dedicated to providing scalable compute, lightning-fast Kubernetes, and managed services that are ready in minutes. Through CivoStack Enterprise and our FlexCore appliance, we empower organizations to maintain total data sovereignty on their own hardware.

Our mission is to make the cloud faster, simpler, and fairer. By providing enterprise-grade NVIDIA GPUs and streamlined model management, we ensure that high-performance AI and machine learning are accessible to everyone. Built for transparency and performance, the Civo Team is here to give you total control over your infrastructure, your data, and your spend.

View author profile
Course complete

Nice work, you finished Kubernetes Setup: get your cluster running.

Your next step is up to you - keep building on what you've learned, or put it into practice on Civo.

Next Course

Kubernetes Concepts: the core building blocks

4 lessons · 11 min

Understand the core concepts behind Kubernetes before writing your first manifest. Covers Kubernetes objects, architecture, namespaces, and labels and selectors.

Put it into practice

Spin up your first cluster on Civo

Get $250 free credit and launch a production-ready Kubernetes cluster in under 90 seconds.