k3s keeps state on the system disk. When / fills up or you want faster storage, the short path is: stop → mv → symlink → start.

The destination (/datadrive here) must exist and be mounted first. Root or sudo.

1
2
3
sudo systemctl stop k3s
sudo systemctl stop k3s-agent   # only if this node is an agent
sudo /usr/local/bin/k3s-killall.sh
1
2
3
sudo mv /run/k3s/ /datadrive/k3s/
sudo mv /var/lib/kubelet/pods/ /datadrive/k3s-pods/
sudo mv /var/lib/rancher/ /datadrive/k3s-rancher/
1
2
3
sudo ln -s /datadrive/k3s/ /run/k3s
sudo ln -s /datadrive/k3s-pods/ /var/lib/kubelet/pods
sudo ln -s /datadrive/k3s-rancher/ /var/lib/rancher
1
2
sudo systemctl start k3s
sudo systemctl start k3s-agent   # agents only
1
2
kubectl get nodes
kubectl get pods -A

What moves

SourceExample destination
/run/k3s//datadrive/k3s/
/var/lib/kubelet/pods//datadrive/k3s-pods/
/var/lib/rancher//datadrive/k3s-rancher/

Working state lives there: containerd, manifests, pods. A half-finished mv or a start against empty paths breaks the cluster.

Traps

/run is tmpfs. After a reboot the /run/k3s symlink is gone. Recreate it from a unit After=local-fs.target, or leave /run/k3s on RAM and do not move it.

k3s-agent does not exist on a server-only node. systemctl stop k3s-agent failing is not a migration failure.

Create the destination parent (mkdir -p /datadrive) and confirm the mount (findmnt /datadrive) before mv. A mv onto an unmounted path leaves the data on the old disk under a new name.

containerd and kubelet sometimes reject a symlink. If the node never goes Ready, switch to a bind mount in /etc/fstab:

1
/datadrive/k3s-rancher  /var/lib/rancher  none  bind  0  0

Cleaner alternative: --data-dir

The official k3s path is --data-dir (default /var/lib/rancher/k3s). On a new node, install with the data-dir already on the large disk. On an existing node, mv + symlink (or bind) of /var/lib/rancher is the shortcut; do not rewrite the unit mid-cluster without a plan.

Write-up of this mv + symlink sequence: How to Move K3s Data to a New Location.