When Storage Exists Outside the Scanner’s Map

This chapter applies the accounting chain to storage in microVM-based containers. The focus is the gap between a host storage object and the path-based, periodic scan that Kubernetes uses for ephemeral-storage decisions: what survives when a file name is removed, what happens when a host path is passed through the VM, and why virtio-fs, virtio-9p, and virtio-blk produce different containment outcomes. The result is a way to distinguish a missing directory entry from released storage and a bounded backing image from an unobserved host filesystem.

Storage accounting sounds more concrete than memory accounting because disk blocks persist. In practice, persistence creates its own ambiguity. A guest sees names and directories. The host retains inodes, descriptors, mounts, backing files, and allocated blocks. Kubernetes makes an eviction decision from the subset of those objects that its accounting model is designed to find. When those views disagree, a pod can appear to be within its ephemeral-storage limit while the node is losing capacity.

A file name is not the storage object

The study describes Kubernetes ephemeral-storage accounting as path-inclusive rather than as a universal inventory of every block associated with a workload. The relevant usage includes selected locations such as a container’s writable layer, its logs, and emptyDir volumes. The kubelet’s eviction logic periodically aggregates usage by traversing filesystems in those accounting domains. That design is practical: it gives the orchestrator a repeatable way to measure ordinary ephemeral data without requiring every guest and host component to expose a perfect object graph.

The design also defines a field of view. A path outside the selected domains may not be counted. A file that no longer has a directory entry may not be found by a scan that follows names. An object exposed through a host passthrough mount may be visible inside the guest but not represented as ordinary usage in the path hierarchy the scanner expects. The accounting number can therefore be internally consistent with the scan while still incomplete as a measure of host disk consumption.

The lifecycle of an unlinked file makes this boundary precise:

stateDiagram-v2 [*] --> NamedPath NamedPath --> OpenDescriptor: open OpenDescriptor --> ScannedUsage: periodic scan while named ScannedUsage --> Charged: usage enters accounting OpenDescriptor --> UnlinkedName: unlink UnlinkedName --> RetainedBlocks: descriptor keeps blocks RetainedBlocks --> ScannerMiss: path scan cannot find name ScannerMiss --> DiskPressure: blocks continue accumulating Charged --> Eviction: visible usage crosses limit RetainedBlocks --> Released: last descriptor closes Released --> [*]

Creating a file gives it a name and an inode. Opening it gives a process a descriptor through which it can continue to access the object. Unlinking removes the name from the directory, but it does not necessarily release the inode or its allocated blocks while the descriptor remains open. The last close can release the object, subject to the filesystem’s behavior, but a periodic name-based scan in the interval can miss it.

This is why “deleted” is an unsafe synonym for “no longer consuming disk.” The guest application can remove the name while retaining a live descriptor.