Andrew Mercer
on this page

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-ip was 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-endpoint at kubeadm init avoids 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.