My main storage array is 839 GB of btrfs sitting on a mdadm RAID5. Last month it was reading half full and I couldn't tell you what half. That bothered me more than a full disk would have, because a full disk at least tells you where the space went.
So I went looking. Twenty minutes later I had my answer: 333 GB of backups from 2024 that I had not touched since, plus a scatter of empty share definitions pointing at folders that no longer existed. I deleted the lot in about seven minutes. The interesting part is not the number. It's that the space had been invisible, and that OpenMediaVault makes the cleanup safe only if you do it the way OMV wants.
Inventory before you touch anything
The rule I hold to on storage is boring on purpose: look before you delete, and write the looking down. Two commands did most of it.
du -h --max-depth=3 /srv/dev-disk-by-uuid-*/ | sort -rh | head -40
find /srv -type d -empty
The first gives you the fat directories ranked by size, three levels deep, which is usually deep enough to name the culprit without drowning in output. The second finds the empty ones, which on an OMV box are often the tell-tale of a share whose data got moved or deleted while the share definition stayed behind.
I put the output in a text file and went through it line by line, tagging each entry as one of three things. Active, meaning something reads or writes it and it stays. Stale, meaning it is real data nobody needs anymore, like those 2024 backups. Orphan stub, meaning OMV still has a shared-folder entry for it but the underlying directory is gone. The 333 GB was almost all stale. The orphan stubs were tiny on disk but they are the part that bites you later, so they went on the list too.
Why I don't hand-edit OMV's config
Here is the thing that catches people. OpenMediaVault keeps its entire configuration in one XML file at /etc/openmediavault/config.xml, and it is right there, readable, editable. You can open it, find the share you want gone, delete the block, save. It will work until the next time OMV regenerates config from that file, which it does through its salt-based backend whenever you change almost anything in the web UI. Your hand edit either gets clobbered or, worse, leaves the running system and the config file disagreeing about what exists.
The supported path is omv-rpc, the same RPC interface the web UI drives. Anything the UI can do, omv-rpc can do from the shell, and it goes through the same validation and the same salt apply. So the destruction gets scripted, and OMV stays the source of truth about its own state.
Enumerate the shared folders first so you are working from UUIDs, not names:
omv-rpc -u admin 'ShareMgmt' 'enumerateSharedFolders' | jq -r '.[] | [.uuid, .name, .reldirpath] | @tsv'
Then, per target I had already confirmed as stale or orphan, delete the share definition by its UUID and apply:
omv-rpc -u admin 'ShareMgmt' 'delete' '{"uuid": "<the-uuid>"}'
omv-rpc -u admin 'Config' 'applyChanges' '{"modules": [], "force": false}'
The actual bytes I removed with a plain rm -rf on the confirmed-stale directories, one at a time, after the share definitions were gone. No script looped over a wildcard. Every deletion named one specific path I had already tagged by hand.
The sentinel UUID gotcha
OMV uses one special UUID, fa4b1c66-ef79-11e5-87a0-0002b3a176b4, as its "not set" reference. When a service, an NFS export, or an SMB share points at a shared folder, it stores that folder's UUID. Delete the folder definition out from under a still-referencing export and OMV does not always stop you, and the reference can fall back to that sentinel. You end up with an NFS export or an SMB share that resolves to nothing, and the symptom is a client mount that hangs or 404s rather than an error at delete time.
The order that avoids it: remove the referencing service first (the NFS export, the SMB share), then the shared-folder definition, then the data. I got this backwards on one entry, saw an export left pointing at the sentinel, and had to walk it back.
What it actually cost
Thirty minutes of inventory. Seven minutes of deletion. The imbalance is the whole lesson. The dangerous work was fast and the safe work was slow, which is the correct ratio and the opposite of how it feels in the moment when you can see 333 GB you want back.
I have put a reminder in the calendar to do this once a year. Not a cron job that deletes things, I am not automating rm -rf against my own data on a schedule. Just the inventory half, the du and the find, so once a year I have to look at what the array is actually holding and decide, on purpose, what stays.
Most homelab storage fills with ghosts. Old backups, half-finished migrations, shares for services you retired. The disk never complains until it is full, and by then the archaeology is harder. Cheaper to look every year while you still remember what the folders were for.

