Renaming a k3s node is not a cosmetic change
I renamed four nodes because the old domain annoyed me. The cluster stayed green for weeks while sitting one reboot away from losing every worker.
I moved four k3s nodes from tpiN.ig3.c.example.com to tpiN.host.example.com because the old domain annoyed me, and I didn’t think about it any harder than that.
k3s derives the node name from the full /etc/hostname, not the short form, and the running processes were still holding their pre rename names.
So kubectl get nodes looked untouched, while the next restart was going to register all four under their new FQDNs. Longhorn keys replicas by node name, so that would have cut every volume loose from the node holding its data.
The cluster stayed green for weeks while sitting one reboot away from losing every worker.
Pin the hosts first
I had deleted the old DNS records too, so the old names were NXDOMAIN and the agents were only still running because their load balancer held a cached address.
cp -n /etc/hosts /etc/hosts.bak.pre-renamecat >> /etc/hosts <<'HOSTS'192.168.178.41 tpi1.host.example.com tpi1192.168.178.42 tpi2.host.example.com tpi2192.168.178.43 tpi3.host.example.com tpi3192.168.178.44 tpi4.host.example.com tpi4HOSTSWe leave them there afterwards, so the cluster does not depend on DNS being up at all.
Pin the node name
This is the actual fix, and you want it in before the hostname changes rather than after.
mkdir -p /etc/rancher/k3sprintf 'node-name: tpi1\n' > /etc/rancher/k3s/config.yamlsystemctl restart k3sOn the workers the agent needs repointing as well, because the server address lives in the unit environment file rather than the config.
sed -i "s|tpi1\.ig3\.c\.example\.com|tpi1.host.example.com|g" \ /etc/systemd/system/k3s-agent.service.envsystemctl daemon-reload && systemctl restart k3s-agentClean up the duplicates
Restarting the agents springs the trap, so four node objects become eight, the real ones going NotReady at 2y143d while impostors on the same hardware come up Ready beside them. Pinning the names brings them back with their ages intact.
kubectl delete node tpi1.host.example.comkubectl patch node tpi1.host.example.com --type=merge \ -p '{"metadata":{"finalizers":null}}'kubectl -n longhorn-system patch nodes.longhorn.io tpi1.host.example.com \ --type=merge -p '{"spec":{"allowScheduling":false}}'The finalizer and the Longhorn scheduling flag are the two reasons a plain delete hangs on you.
The certificate
Three things that look like they should remove the old name do not: dropping --tls-san does nothing, deleting the certificate files does nothing because they come back from the etcd bootstrap bundle, and k3s certificate rotate gives you a new certificate carrying the same dead name.
The annotations are only dynamiclistener’s request list. The certificate lives in the secret’s tls.crt, and dynamiclistener rewrites that to add a name, never to remove one.
kubectl -n kube-system annotate secret k3s-serving \ "listener.cattle.io/cn-tpi1.ig3.c.example.com-"kubectl -n kube-system delete secret k3s-servingrm -f /var/lib/rancher/k3s/server/tls/dynamic-cert.jsonsystemctl restart k3sAll three of those, in that order, and no CA material gets touched so nothing else needs reissuing.
$ kubectl -n kube-system get secret k3s-serving -o jsonpath='{.data.tls\.crt}' \ | base64 -d | openssl x509 -noout -text | grep -A2 "Subject Alternative Name"Ready is a weak signal
After the reboots every node reported Ready and every pod was scheduled, while Longhorn volumes sat wedged in attaching because cross node pod traffic was gone.
Routes were right, neighbour entries were right, and the forwarding database on flannel.1 was empty on all four nodes.
$ bridge fdb show dev flannel.12a:1b:3c:4d:5e:02 dst 192.168.178.42 self permanent2a:1b:3c:4d:5e:03 dst 192.168.178.43 self permanent2a:1b:3c:4d:5e:04 dst 192.168.178.44 self permanentThat is one entry per peer, and empty means cross node pod traffic is dead however healthy the node list looks, with a k3s-agent restart repopulating it.
The real gap this exposed is that node level config is the one part of this cluster with no version control and no backup, and that part is unchanged.