A node paged NodeFilesystemFilesFillingUp at 67% inodes used (predict_linear trajectory alert) while disk was only 83% — image GC is the only early mechanism kubelet has, and its thresholds (85%/80%) are byte-based; the only inode threshold (nodefs/imagefs.inodesFree, 5% free, Linux-only) is the hard-eviction floor, so first inode response is pod eviction.
ext4 fixes inode count at mkfs via inode_ratio (16,384 default, node appears ~8192); with all-small files, inodes exhaust at ~25–50% of disk — reproduced with mkfs.ext4 -d on a 64M image (4,085 one-byte files, "Could not allocate inode" with two-thirds of blocks free).
The culprit was containerd's overlayfs snapshot store: an image carrying >40,000 files (21,553 in node_modules/@mui/icons-material per snapshot), duplicated per layer digest since content-store dedup doesn't extend to unpacked snapshots; missing .dockerignore and cacheless CI builds with new timestamps create new digests each commit.
Fixes: multi-stage Dockerfile shipping only build output; add flat inode alert (~80% used, 30m for) plus a PromQL query ranking nodes by inode-usage-minus-disk-usage; cleanup via crictl rmi --prune (avoid crictl rm --all in cron).