The idea is appealing: declare a 500 GB disk that only occupies the 40 GB actually written. That is thin provisioning, and on the write side it delivers exactly what it promises.
The problem is the other direction. You delete 200 GB inside a virtual machine, you look at the hypervisor, and usage has not moved a byte. Nothing is broken. Nobody simply told the layer below.
Deleting a file frees nothing
A deletion is local to the guest filesystem: an entry disappears from a table, and the blocks are marked reusable for that filesystem. No message travels downward.
From the hypervisor's point of view, those blocks were written at least once, so they are allocated. It has no way whatsoever to guess they no longer hold anything useful. Thin provisioning allocates on first write and never deallocates on its own.
Reclaiming space requires an explicit declaration: the TRIM command, meaning "these blocks are free, take them back".
A chain, and one broken link is enough
This is where most setups fail, because the reclaim has to cross every layer:
- The guest filesystem must issue TRIMs — continuously or on a periodic pass.
- The virtual disk controller must be able to forward them, and the corresponding option must be enabled on the disk.
- The image format or volume must support deallocation.
- The physical storage must honour the command.
A single missing link and nothing propagates. And here is the perverse part: no error is reported. The command leaves, gets lost on the way, and the space stays occupied. You go hunting for a fault where there is only an unchecked option.
Continuous or periodic?
Continuous mode issues a discard on every deletion. It is immediate, but it places an extra operation on the write path with variable latency depending on the layers crossed. On a virtualised stack, the behaviour is sometimes frankly poor.
Periodic mode walks the free space at regular intervals and frees everything at once. It is the default on most modern distributions, and it is the right choice in almost every case: the cost is concentrated into a window you choose rather than spread across every write.
One thing to watch: a first pass on a volume that has never been trimmed can free hundreds of gigabytes at once and generate significant I/O load. Run it outside sensitive hours.
The zero-fill workaround: handle with care
With no working TRIM, the most shared recipe is to fill the free space with a large file of zeros, delete it, and hope the storage layer detects those null blocks and deallocates them.
That works on some storage. On others, exactly the opposite happens: writing zeros is writing data. On a thin volume managed by the logical volume manager, you massively reallocate the blocks you meant to return — and you can fill the pool while trying to empty it.
Before applying that recipe, check how your storage layer actually behaves. Repairing the TRIM chain is almost always better than working around it.
The real risk: pool saturation
All of this would stay cosmetic if unreturned space had no serious consequence.
A thin pool allows overprovisioning: the sum of declared volumes may exceed physical capacity. That is the whole point, and it works as long as machines do not all write at maximum.
But if space is never returned, real usage only ever grows. The day the pool is full, writes fail — not for one machine, but for every machine sharing the pool. Guests drop to read-only at best, corrupt at worst.
A thin pool must therefore be monitored on its real usage, with an alert triggered well before saturation. It is the highest-return monitoring on a virtualised infrastructure, and one of the most frequently forgotten.
How to proceed
If space does not come back, walk the chain in order rather than trying recipes: check that the guest issues TRIMs, that the virtual controller forwards them, that discard is enabled on the disk, and that the storage honours them. The missing link is almost always the second one — and it announces itself with no message at all.
Comments