In production you'd rebuild the node rather than re-address it. In a small homelab, after a network change, editing in place is quicker. These steps target a single control-plane kubeadm cluster with stacked etcd.
The old IP appears in three places: config files, certificate SANs, and in-cluster ConfigMaps. All three need updating.
1. Back up¶
sudo cp -a /etc/kubernetes /etc/kubernetes.bak-$(date +%F)
cp ~/.kube/config ~/.kube/config.bak
2. Replace the IP in config files¶
Files that contain the control-plane IP:
~/.kube/config/etc/kubernetes/admin.conf/etc/kubernetes/super-admin.conf(Kubernetes 1.29+)/etc/kubernetes/controller-manager.conf/etc/kubernetes/scheduler.conf/etc/kubernetes/kubelet.conf/etc/kubernetes/manifests/etcd.yaml/etc/kubernetes/manifests/kube-apiserver.yaml/var/lib/kubelet/kubeadm-flags.env(only if--node-ipwas set)
Replace the IP in every file that contains it. The dots are escaped so 10.0.0.1 doesn't also match 10.0.0.10, and each file gets a .bak copy:
old_ip="<old-ip>"; new_ip="<new-ip>"
sudo grep -rlE "\b${old_ip//./\\.}\b" /etc/kubernetes /var/lib/kubelet/kubeadm-flags.env ~/.kube/config \
| xargs -d '\n' -r sudo sed -i.bak "s/\b${old_ip//./\\.}\b/${new_ip}/g"
Move the .bak files out of /etc/kubernetes/manifests/ afterward. The kubelet tries to run every file in that directory as a static pod:
sudo mkdir -p /root/manifest-backups
sudo mv /etc/kubernetes/manifests/*.bak /root/manifest-backups/
3. Regenerate certificates that contain the old IP¶
The API server and etcd serving certificates have the node IP in their SANs. Clients reject them after the move with x509: certificate is valid for <old-ip>, not <new-ip>.
Check what's there now:
sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -ext subjectAltName
Move the affected certs aside (keep the CAs), then let kubeadm regenerate only what's missing:
cd /etc/kubernetes/pki
sudo mkdir -p /root/pki-old
sudo mv apiserver.crt apiserver.key /root/pki-old/
sudo mv etcd/server.crt etcd/server.key etcd/peer.crt etcd/peer.key /root/pki-old/
sudo kubeadm init phase certs all --apiserver-advertise-address=${new_ip}
certs all skips any certificate that already exists, so only the ones you moved get recreated, signed by the existing CAs. Re-run the openssl check to confirm the new IP is in the SANs.
4. Restart and verify¶
sudo systemctl restart kubelet
sudo crictl ps # wait for etcd and kube-apiserver to come back
kubectl get nodes -o wide
5. Update in-cluster references¶
The old address is also stored in ConfigMaps that new joins and kube-proxy read:
kubectl -n kube-system edit cm kubeadm-config # controlPlaneEndpoint / advertiseAddress, if set to the IP
kubectl -n kube-system edit cm kube-proxy # server: in kubeconfig.conf
kubectl -n kube-public edit cm cluster-info # server: in the embedded kubeconfig
kubectl -n kube-system rollout restart ds kube-proxy
6. Repoint the workers¶
On each worker, update the API server address the kubelet talks to, then restart it:
sudo sed -i "s/${old_ip//./\\.}/${new_ip}/g" /etc/kubernetes/kubelet.conf
sudo systemctl restart kubelet
If a worker's own IP changed, it only needs its kubelet restarted, plus an update to --node-ip if that was pinned (see MetalLB troubleshooting).
Notes¶
- Using a DNS name as the
--control-plane-endpointatkubeadm initavoids most of this. A future IP change becomes a DNS update plus step 3. - Single-member etcd usually comes back fine with the new address. If etcd logs complain about the peer URL, update it with
etcdctl member update <member-id> --peer-urls=https://<new-ip>:2380.