Reclaiming disk space when you’re already out of disk space ; what’s the right order of operations?
This comes up often enough that I want to hear how others actually handle it, because the standard advice has a chicken-and-egg problem.
The scenario: heavily bloated tables from accumulated dead tuples, plus a pile of indexes that may or may not be earning their keep, and not much free space on the volume. The usual recommendations are pg_repack, pg_squeeze, or VACUUM FULL but all three want temporary space you don’t have. pg_repack in particular needs a new table, new indexes, and a log table for changes during the copy, so peak usage can exceed VACUUM FULL. You reach for it to avoid the ACCESS EXCLUSIVE lock and get surprised by the disk cost instead.
A few things I’d like to argue out:
1. Is dropping unused indexes always the correct first move? It’s the only step that returns space with no temporary copy. But idx_scan = 0 in pg_stat_user_indexes isn’t proof stats reset, replicas have their own counters, and constraint-backing indexes can look idle. What’s your bar before you actually drop one?
2. Does REINDEX buy you anything real here, or is it a distraction? It does nothing for heap bloat, so it can’t replace a repack. But it’s granular , one index at a time, so you only need space for the largest single index rather than the whole relation. Is incremental reclaim through REINDEX (or REINDEX CONCURRENTLY) a legitimate strategy when you’re constrained, or just delaying the inevitable?
3. When is plain VACUUM the actual answer? It won’t return space to the OS except by truncating trailing pages, but it makes the space reusable and stops further file growth. How often is the real problem “this table keeps growing” rather than “this table is bloated”?
4. What’s holding the xmin horizon? If dead tuples accumulate faster than vacuum reclaims them, repacking is a treadmill. Long-running transactions, inactive replication slots, orphaned prepared transactions. An abandoned slot from a decommissioned replica seems like the most common culprit , how often does this turn out to be the root cause rather than the tooling?
5. Does pg_repack --tablespace solve the constraint in practice? Repacking into a different mount sidesteps the space problem if you have another volume available. Is anyone using this routinely, or does the I/O cost make it impractical?
My instinct is: drop genuinely unused indexes, fix whatever is blocking vacuum, then REINDEX incrementally, and only reach for VACUUM FULL when you can take the outage and can’t get space anywhere else. Interested in where people disagree.