Three-node k3s HA cluster went crash-looping for two days — Ceph-backed boot disks, a full root disk, and stale storage-attachment state stacked on top of each other. Here’s the chain, the dead ends, and the fix.
2026-08-15 to 2026-08-16
Restore a k3s HA cluster that had gone intermittently crash-looping. Workloads across the cluster were failing in a mix of CreateContainerError, ContainerStatusUnknown, and image-pull states, and repeated pod deletion wasn’t fixing anything.
A chain, not one fault:
Multi-Attach error for volume ... Volume is already exclusively attached, VolumeAttachment objects stuck terminating, one workload reporting a missing CephFS CSI driver.failed to mount overlay: lowerdir ... snapshots/6133/fs: no such file or directoryimagePullPolicy: Never with no local image, a registry cert hostname mismatch, registry auth failure, then a custom image built with apt-get on an Alpine base.Didn’t work, don’t repeat: deleting/recreating storage-bound pods, cutting VM memory, waiting for self-recovery, deleting VolumeAttachments without checking state first, trusting kubectl get nodes: Ready as proof of health.
Actually worked:
VolumeAttachments by the underlying PV; wait for a normal detach.terminating, remove its finalizer.ctr metadata before touching it. Only delete ones confirmed orphaned (“snapshot not found”). Four directories on Navia qualified.Structural fixes shipped:
Succeeded/Failed pods.Cluster stable since. Open follow-ups: protect Infisical (and other infra-critical workloads) from utilization-based eviction until fully validated, add ephemeral-storage requests/limits with DiskPressure alerting, add a read-only audit for containerd snapshots missing from metadata, validate Ceph CSI health end-to-end, and add a fourth Proxmox node so VMs/OSDs stop competing for the same three boxes.
Ready says nothing about storage-layer health — check CSI state separately.# check disk / containerd usage
df -h /
du -sh /var/lib/rancher/k3s/agent/containerd
# verify a snapshot is actually orphaned before deleting it
ctr -n k8s.io snapshots info <snapshot-id>
# "snapshot not found" == confirmed orphan, safe to remove
# inspect stuck volume attachments
kubectl get volumeattachment -o wide
kubectl describe volumeattachment <name>
# remove a finalizer on a confirmed-stale VolumeAttachment (last resort, after
# confirming the workload is scaled to zero and no pod references the PVC)
kubectl patch volumeattachment <name> -p '{"metadata":{"finalizers":null}}' --type=merge
# housekeeping (deployed as daily systemd units)
journalctl --vacuum-size=500M
k3s crictl rmi --prunepaste commands here