Thin provisioning: why freed space never comes back

You delete 200 GB inside a VM and the hypervisor doesn't see a single byte return. TRIM, discard, unmap: the full chain you have to enable end to end to reclaim space.

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:

  1. The guest filesystem must issue TRIMs — continuously or on a periodic pass.
  2. The virtual disk controller must be able to forward them, and the corresponding option must be enabled on the disk.
  3. The image format or volume must support deallocation.
  4. 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.

The most frequent culprit: the virtual disk controller type. Some emulated controllers do not forward discards, and the corresponding option is rarely enabled by default. The guest does its job, the storage would do its own, but the message never arrives.

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.

Frequently asked questions

Why doesn't space return after deleting files inside a VM?
Because deletion is a guest filesystem operation: it removes an entry from a table, it tells nobody underneath. From the hypervisor's point of view those blocks were written once, so they are allocated, and nothing indicates they are no longer in use. The guest must explicitly issue a TRIM command meaning "these blocks are free", and every layer it crosses must forward it down to the storage.
Which link fails most often?
The virtual disk controller. Not every emulated controller can forward the discard command, and the discard option usually has to be enabled explicitly on the disk. The classic outcome: the guest issues TRIMs correctly, the underlying storage would honour them without trouble, but the controller does not pass them on. No error appears anywhere — the space simply does not come back.
Should I use continuous or periodic TRIM?
Periodic is preferable in the vast majority of cases. Continuous mode issues a discard on every deletion, adding latency to the write path and behaving poorly across some layer stacks. A scheduled pass, typically weekly, handles all freed space at once, at a time you choose. It is the default on most modern distributions, and it is a sound one.
Does filling the disk with zeros reclaim space?
This is the most widespread and most misleading workaround. It works on storage that can detect null blocks and deallocate them. On others — notably thin volumes managed by the logical volume manager — writing zeros means writing data: you reallocate exactly the blocks you wanted to free, and things get worse. Check how your storage layer behaves before applying that recipe.
What happens if a thin pool fills up?
That is the real danger, and it is not cosmetic. A thin pool can be overprovisioned: the sum of advertised volumes exceeds real capacity. That works as long as everyone does not write at once. The day the pool fills, writes fail: guest systems go read-only or corrupt. A thin pool must be monitored with an alert well before saturation.

Did you enjoy this article?

Comments

Morgann Riu

Cybersecurity and Linux administration expert. I help companies secure and optimize their critical infrastructures.

Back to the blog

Checklist Sécurité Linux

30 points essentiels pour sécuriser un serveur Linux. Recevez aussi les nouveaux tutoriels par email.

Pas de spam. Désabonnement en 1 clic.