kubeadm and node operations cheat sheet
Building, joining, upgrading and repairing a cluster, plus the node-level commands for when kubectl itself is not working. Ordered by the operation you are performing.
- Kubernetes1.36.4
- kubeadm1.36.4
- containerd2.2.6
- etcd3.6.8
- Commands42
- Reviewed21 August 2026
Before init: host prerequisites
-
sudo swapoff -aDisable swap for the current boot. Also remove the swap line from /etc/fstab or it returns on reboot.
The kubelet refuses to start with swap on unless failSwapOn is set to false. Disabling it is the normal answer.
-
sudo modprobe br_netfilter overlayLoad the kernel modules the CNI and the runtime need. Persist them in /etc/modules-load.d.
-
sysctl net.ipv4.ip_forward net.bridge.bridge-nf-call-iptablesCheck the two sysctls Pod networking depends on. Both must be 1.
Set them in /etc/sysctl.d so they survive a reboot; a runtime-only change is the classic post-reboot break.
-
sudo containerd config default | sudo tee /etc/containerd/config.tomlWrite a default containerd config, then set SystemdCgroup = true in it.
SystemdCgroup must match the kubelet's cgroup driver. A mismatch produces Pods that start and then die under load.
-
kubeadm config images pullPre-pull the control plane images so init is not waiting on the network.
Creating a cluster
-
sudo kubeadm init --pod-network-cidr=10.244.0.0/16Create a single control plane cluster. The CIDR must not overlap your node network.
-
sudo kubeadm init --control-plane-endpoint <host>:<port> --upload-certsCreate a cluster that more control plane nodes can join later.
Both flags must be on the first init. Adding controlPlaneEndpoint afterwards means reissuing certificates and rewriting every kubeconfig.
-
mkdir -p ~/.kube && sudo cp /etc/kubernetes/admin.conf ~/.kube/config && sudo chown $(id -u):$(id -g) ~/.kube/configGive your user a kubeconfig.
This is a copy. Renewing certificates later updates admin.conf and not this file, which is how kubectl breaks on a cluster whose certificates were renewed.
-
kubectl taint node <name> node-role.kubernetes.io/control-plane:NoSchedule-Remove the control plane taint so workloads can run on a single-node cluster.
-
kubectl -n kube-system get cm kubeadm-config -o jsonpath='{.data.ClusterConfiguration}'The cluster configuration kubeadm recorded, including controlPlaneEndpoint and the CIDRs.
Joining nodes
-
sudo kubeadm token create --print-join-commandA fresh join command for a worker, with a new 24-hour token.
Tokens expire after 24 hours by default. A join failing on a cluster built yesterday is usually this.
-
sudo kubeadm init phase upload-certs --upload-certsRe-upload the control plane certificates and print a new certificate key.
Uploaded certificates are deleted two hours after init, so a control plane join later needs this first.
-
sudo kubeadm join <endpoint> --token <t> --discovery-token-ca-cert-hash sha256:<h>Join as a worker.
-
sudo kubeadm join <endpoint> --token <t> --discovery-token-ca-cert-hash sha256:<h> --control-plane --certificate-key <k>Join as an additional control plane, which also adds an etcd member.
One node at a time, and only while the cluster is healthy: the join is itself a write to etcd, and going from one member to two temporarily tolerates no failures.
-
kubeadm token listExisting bootstrap tokens and their expiry.
Upgrading
-
sudo kubeadm upgrade planWhat versions you can upgrade to and what it will change. Reads only.
-
sudo kubeadm upgrade apply v1.36.4Upgrade the control plane on the first node. Also renews certificates.
A cluster upgraded at least once a year never meets certificate expiry, because this renews them as a side effect.
-
sudo kubeadm upgrade nodeUpgrade the local component configuration on every other node, control plane or worker.
-
sudo apt-mark unhold kubeadm && sudo apt-get install -y kubeadm=<version> && sudo apt-mark hold kubeadmThe package dance: the hold is what stops an unplanned upgrade from a routine apt upgrade.
Order matters. Upgrade kubeadm, run the upgrade, then upgrade kubelet and kubectl and restart the kubelet.
-
sudo systemctl daemon-reload && sudo systemctl restart kubeletPick up a new kubelet binary or unit configuration.
Certificates
-
sudo kubeadm certs check-expirationEvery managed certificate with its expiry, and the CAs separately.
Leaf certificates last one year; the CAs last ten. EXTERNALLY MANAGED yes means kubeadm cannot renew it.
-
sudo kubeadm certs renew allRenew every leaf certificate, including the ones embedded in /etc/kubernetes/*.conf.
Works without a functioning API server, because it signs locally from the CA keys. That is what makes it a recovery tool.
-
sudo kubeadm certs renew apiserverRenew one certificate. Used after adding a name to apiServerCertSANs.
-
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates -ext subjectAltNameRead validity and the SAN list straight off the certificate.
A client connecting by a name absent from the SANs fails verification, and the error blames the certificate rather than the address.
-
echo | openssl s_client -connect 127.0.0.1:6443 | openssl x509 -noout -datesThe certificate actually being served, as opposed to the one on disk.
The only way to confirm a component restarted and picked up a renewed certificate.
-
kubectl config view --raw -o jsonpath='{.users[0].user.client-certificate-data}' | base64 -d | openssl x509 -noout -datesThe expiry of the client certificate in your own kubeconfig.
Run this on any Unauthorized error before looking at RBAC.
etcd
-
sudo etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key member list -w tableThe etcd members, their peer and client addresses, and whether any is a learner.
etcdctl is not shipped with kubeadm. Install the release matching the etcd image the cluster runs.
-
sudo etcdctl ... endpoint health --clusterAsk every member whether it can commit a proposal.
`Error: unhealthy cluster` means at least one member is unhealthy, not that quorum is lost. Read the per-endpoint lines.
-
sudo etcdctl ... endpoint status --cluster -w tablePer-member version, database size and which one is the leader.
-
sudo etcdctl ... snapshot save /var/backups/etcd-$(date +%F).dbTake a snapshot. The only thing that protects the cluster's data.
A snapshot you have not restored from is a hypothesis. Rehearse the restore.
-
sudo etcdctl snapshot restore <file> --data-dir /var/lib/etcd-newWhole-hostRestore into a fresh data directory, then point the etcd static Pod at it.
Never restore over a live data directory, and stop the API server first.
-
sudo etcdctl ... member remove <id>CautionRemove a dead member before joining its replacement.
Skipping this leaves the cluster counting a member that will never vote, which quietly reduces how many failures it tolerates.
On the node, when kubectl will not work
-
sudo systemctl status kubeletWhether the kubelet is running. Everything on a node depends on it.
-
sudo journalctl -u kubelet -n 50 --no-pagerThe kubelet's own log, which is where node-level failures are explained.
The first place to look when a node is NotReady or the control plane is unreachable.
-
sudo crictl psRunning containers, from the runtime rather than the API. Works with the API server down.
-
sudo crictl ps -a --name kube-apiserverIncluding exited containers, which is how you find one that is crash-looping.
-
sudo crictl logs <container-id>A container's logs without the API server.
For a static Pod that never becomes healthy, this is the only way to read why.
-
sudo ls /etc/kubernetes/manifests/The static Pod manifests: etcd, kube-apiserver, kube-controller-manager, kube-scheduler.
-
sudo mv /etc/kubernetes/manifests/kube-apiserver.yaml /root/ && sleep 20 && sudo mv /root/kube-apiserver.yaml /etc/kubernetes/manifests/CautionRestart one control plane component: the kubelet stops the Pod when the file leaves and starts it when it returns.
The precise way to restart after renewing certificates or changing a flag, and to simulate a component failing.
-
sudo ls /etc/cni/net.d/ /opt/cni/bin/The CNI config the kubelet reads and the plugin binaries it executes.
Empty /etc/cni/net.d is why Pods sit in ContainerCreating with a CNI error. It means the CNI DaemonSet has not run here.
-
sudo kubeadm reset -fWhole-hostUndo kubeadm on this node: stop the components, remove the manifests and the etcd data.
Does not clean /etc/cni/net.d, the CNI interfaces or iptables rules. Remove those by hand or the rebuild inherits them.
-
kubectl delete node <name>DestructiveRemove the Node object after resetting the machine.
On a control plane node, also remove its etcd member.
No command matches that search.