· via dev.to (home feed)
PostgreSQL 19 adds native REPACK to reclaim table bloat without locking
PostgreSQL 19 unifies VACUUM FULL and CLUSTER into a native REPACK command whose CONCURRENTLY mode rewrites bloated tables while they stay online.

PostgreSQL 19 introduces REPACK, a built-in command that merges VACUUM FULL and CLUSTER into a single operation and adds a CONCURRENTLY mode that reclaims wasted disk space without taking a table fully offline. According to a dev.to post, the feature removes a trade-off database administrators have faced for years: choosing between reclaiming space and keeping a table available.
The bloat problem REPACK addresses
After large deletes or update-heavy workloads, PostgreSQL tables accumulate dead tuples. While autovacuum cleans them up, it cannot return the occupied space to the operating system, as the dev.to post explains. Until now, DBAs had two imperfect options: VACUUM FULL, which rewrites the table into a new file but holds an ACCESS EXCLUSIVE lock for the entire rewrite, or the third-party pg_repack extension, which only works if its version, permissions and maintenance window line up across every environment.
CLUSTER does the same rewrite but also reorders rows by an index, which helps range scans. Both commands fundamentally perform the same job — copying live tuples into a new file and swapping it in — differing only in whether index order is preserved.
What REPACK does
REPACK treats the two older commands as modes of one operation, giving the PostgreSQL project a single place to build shared features such as concurrency. It copies live rows, rebuilds every index into new files, and swaps the new files in.
As of PostgreSQL 19 Beta 2, the command supports VERBOSE, ANALYZE and CONCURRENTLY options. A plain REPACK on a table behaves like VACUUM FULL, while adding USING INDEX makes it behave like CLUSTER. Specific columns can also be named, which the author suggests is useful for reclaiming storage after dropping a wide column. VERBOSE shows progress output and ANALYZE gathers fresh statistics after the rewrite. The author cautions that the official PostgreSQL 19 release notes and documentation are the sources of record as the beta evolves.
How CONCURRENTLY keeps tables online
The headline feature is REPACK (CONCURRENTLY). Instead of holding an ACCESS EXCLUSIVE lock during the copy, it captures inserts, updates and deletes that occur while the table is being copied using logical decoding, then replays them onto the new copy just before the final file swap. The exclusive lock is only needed for that last step, and it is held briefly.
What the hands-on test showed
The author built a one-million-row table with a primary key and two secondary indexes, then simulated churn: every row updated twice, then half the rows, then a third, followed by a 10% delete. The table grew to 1341 MB with 241 MB of indexes — 1582 MB total — despite holding fewer than a million live rows.
REPACK (VERBOSE, ANALYZE) completed in about 27 seconds and reported removable and nonremovable row versions along with CPU, buffer and WAL statistics. Afterward the table was 370 MB, the indexes 89 MB, and the total 459 MB.
Monitoring and limitations
A new system view, pg_stat_progress_repack, lets you watch a running repack from another session. It exposes the current phase, the command, relation ID, index in use, heap tuples scanned, inserted, updated and deleted, block progress and index rebuild count — so an operator can tell which stage a job is in rather than guessing whether it is stuck.
CONCURRENTLY mode comes with restrictions, per the post. It cannot be used when the table is UNLOGGED, when the table is partitioned (though individual partitions can be repacked one at a time), when the table lacks a primary key and index-based replica identity, or when the command runs inside a transaction block. Any repack, concurrent or not, requires the MAINTAIN privilege on the table — a consideration for lower-privileged migration or automation users, while superusers need no permission changes.
Is pg_repack still needed
Yes, according to the author. It remains the only online option on PostgreSQL 18 and earlier, and on version 19 its per-partition batch tooling and its track record on very large, long-running jobs still make it worth keeping for edge cases, particularly whole partitioned tables. For the common case of a single logged table with a primary key bloated by update or delete churn, native REPACK (CONCURRENTLY) removes both an extension dependency and a maintenance window.
Why it matters
Table bloat has always been well understood; what was missing was a fix that did not force a choice between reclaiming space and keeping a table online. By moving the online rewrite into the core server with a dedicated progress view, PostgreSQL 19 turns what used to be a scheduled outage — or a dependency on a third-party extension — into a routine, observable background operation for many workloads.
- #postgresql
- #databases
- #database-administration
- #sql
- #open-source