1.4 Initializing Clusters with Kubeadm

Key Takeaways

  • kubeadm is the standard Kubernetes bootstrapping CLI that automates certificate generation, static pod manifest creation, kubeconfig files, and node registration.
  • For the common kubeadm path, disable swap or explicitly configure kubelet swap support, load networking modules required by the chosen CNI, enable IPv4 forwarding, and keep kubelet and runtime cgroup drivers aligned.
  • The kubeadm init execution runs through sequential phases: preflight, certs, kubeconfig, kubelet-start, control-plane, etcd, upload-config, upload-certs, mark-control-plane, bootstrap-token, and addon.
  • A freshly initialized control plane node remains in the NotReady state until a Container Network Interface (CNI) plugin (e.g., Calico or Flannel) is deployed.
  • Worker nodes join the cluster via kubeadm join using a bootstrap token and a SHA-256 discovery token CA certificate hash.
Last updated: August 2026

1.4 Initializing Clusters with Kubeadm

kubeadm is the official Kubernetes project tool designed to establish a minimum viable, secure, best-practice Kubernetes cluster with full lifecycle management capabilities. Rather than manually generating dozens of X.509 certificates, configuring complex systemd unit files, and writing static pod manifests from scratch (the "Kubernetes The Hard Way" approach), kubeadm standardizes the bootstrap process into two fundamental commands:

  • kubeadm init: Bootstraps the primary control plane node.
  • kubeadm join: Attaches additional control plane or worker nodes to an existing cluster.

1. Host Operating System & Node Prerequisites

Before running kubeadm, every host machine (control plane and worker) must satisfy strict OS and networking prerequisites.

+-----------------------------------------------------------------------------+
|                        NODE PRE-BOOTSTRAP CHECKLIST                         |
|                                                                             |
|   1. DISABLE LINUX SWAP MEMORY                                              |
|      $ swapoff -a                                                           |
|      $ sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab                        |
|                                                                             |
|   2. LOAD KERNEL OVERLAY & BRIDGE MODULES                                   |
|      $ modprobe overlay                                                     |
|      $ modprobe br_netfilter                                                |
|                                                                             |
|   3. CONFIGURE SYSCTL BRIDGING & IP FORWARDING                              |
|      net.bridge.bridge-nf-call-iptables  = 1                                |
|      net.bridge.bridge-nf-call-ip6tables = 1                                |
|      net.ipv4.ip_forward                 = 1                                |
|      $ sysctl --system                                                      |
|                                                                             |
|   4. CONFIGURE CONTAINER RUNTIME CGROUP DRIVER                              |
|      containerd config default -> SystemdCgroup = true                      |
|      $ systemctl restart containerd                                         |
+-----------------------------------------------------------------------------+

Step-by-Step Prerequisite Configuration:

# 1. Use the default kubeadm path: disable swap. Kubernetes v1.35 can also use swap when kubelet is deliberately configured with failSwapOn: false and an appropriate memorySwap policy:
sudo swapoff -a
sudo cp /etc/fstab /etc/fstab.pre-kubeadm
sudo sed -i '/[[:space:]]swap[[:space:]]/ s/^/#/' /etc/fstab

# 2. Persist Kernel Modules Required for CNI Bridging:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 3. Apply Sysctl Parameters for Packet Filtering:
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

sudo sysctl --system

# 4. Configure containerd with systemd Cgroup Driver:
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
sudo systemctl restart containerd

[!IMPORTANT] Cgroup Driver Matching: The container runtime and kubelet must use compatible cgroup drivers. The systemd driver is recommended on systemd-based hosts, and kubeadm defaults kubelet to it. Configure containerd consistently; a mismatch can prevent kubelet from managing containers correctly.


2. The kubeadm init Multi-Phase Bootstrap Pipeline

When kubeadm init is executed, it runs through a strictly orchestrated sequence of internal bootstrap phases:

  1. preflight: Validates kernel parameters, CPU cores (minimum 2), RAM (minimum 2GB), swap status, port availability (6443, 2379, 2380, 10250, 10259, 10257), and CRI socket connectivity.
  2. certs: Generates self-signed Certificate Authorities (CAs) and certificates for all components inside /etc/kubernetes/pki (ca.crt, apiserver.crt, apiserver-kubelet-client.crt, front-proxy-ca.crt, etcd/ca.crt).
  3. kubeconfig: Generates administrative and internal kubeconfig files in /etc/kubernetes/ (admin.conf, kubelet.conf, controller-manager.conf, scheduler.conf).
  4. kubelet-start: Writes the initial /var/lib/kubelet/config.yaml and environment file /var/lib/kubelet/kubeadm-flags.env, then starts the local kubelet daemon via systemd.
  5. control-plane: Generates static pod manifests in /etc/kubernetes/manifests/ for kube-apiserver, kube-controller-manager, and kube-scheduler.
  6. etcd: Generates the static pod manifest for the local stacked etcd instance.
  7. upload-config: Uploads the kubeadm-config and kubelet-config ConfigMaps into the kube-system namespace for future reference.
  8. upload-certs: If --upload-certs is specified, encrypts control plane certificates and uploads them to a Secret in kube-system with a 2-hour decryption key.
  9. mark-control-plane: Labels the node as a control plane (node-role.kubernetes.io/control-plane:NoSchedule) to prevent regular workloads from scheduling on it.
  10. bootstrap-token: Creates a token in kube-system used by worker nodes to authenticate during join.
  11. addon: Deploys kube-proxy (as a DaemonSet) and CoreDNS (as a Deployment) into kube-system.

3. Initializing the Primary Master Node

To initialize the first control plane node with standard pod CIDR networking:

sudo kubeadm init \
  --pod-network-cidr=10.244.0.0/16 \
  --apiserver-advertise-address=192.168.1.10 \
  --cri-socket=unix:///run/containerd/containerd.sock

Setting Up kubectl Access for the User:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

4. Deploying the Container Network Interface (CNI)

Upon initialization, running kubectl get nodes shows the node in a NotReady status. This is because no pod network interface has been configured.

# Use the task-provided or current vendor-supported manifest for the chosen CNI.
kubectl apply -f <cni-manifest-or-operator-resource>

# Watch reconciliation instead of assuming a fixed delay.
kubectl get pods -n kube-system -w
kubectl get nodes

The node becomes Ready only after the selected CNI is installed and healthy. Installation commands and compatible versions differ among Calico, Flannel, and Cilium, so follow the supplied task or the current provider documentation.


5. Joining Worker Nodes (kubeadm join)

To attach worker nodes to the cluster, execute the printed join command on each worker:

sudo kubeadm join 192.168.1.10:6443 \
  --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:8f4c2e6b7a9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f

Regenerating Tokens and Hashes:

If the 24-hour join token has expired, generate a new join command from the control plane:

# Generate new token with full join command:
sudo kubeadm token create --print-join-command

# Or manually calculate the CA cert hash:
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | \
  openssl rsa -pubin -outform der 2>/dev/null | \
  openssl dgst -sha256 -hex | sed 's/^.* //'
Loading diagram...
kubeadm Initialization and Node Join Sequence
Test Your Knowledge

An administrator initializes a single-node control plane using kubeadm init --pod-network-cidr=10.244.0.0/16. When running kubectl get nodes, the node reports status NotReady, and CoreDNS pods remain in the Pending state. What is the cause and required resolution?

A
B
C
D
Test Your Knowledge

A worker node fails to join the Kubernetes cluster during kubeadm join, throwing a bootstrap token validation error because the original token expired after 24 hours. Which command should be executed on the control plane to generate a new token and print the complete join command?

A
B
C
D
Test Your Knowledge

For a common bridge-and-iptables CNI setup, which Linux settings enable bridged packet inspection and IPv4 forwarding?

A
B
C
D