Quick answer for pros
Check pg_replication_slots, look for slots with active = false. Drop them with SELECT pg_drop_replication_slot('slot_name');. Then set max_slot_wal_keep_size = -1 in postgresql.conf and reload. Done.
What's going on
Replication slots are great for ensuring a standby never misses WAL data. But if a standby goes down and doesn't come back, the slot holds on to all WAL files since it last connected. This eats disk space fast — I've seen a 500GB drive fill up in hours.
This usually happens after a network outage, a standby server crash that never recovers, or someone forgetting to drop a slot when decommissioning a replica. The PostgreSQL logs will show errors like WAL segment not found or the disk fills up until pg_wal hits 100%.
Step-by-step fix
- Identify the culprit slot. Run this on the primary:
Look for slots whereSELECT slot_name, active, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) / 1024 / 1024 AS MB_held FROM pg_replication_slots;activeisfandMB_heldis huge. - Drop the dead slot. Replace
dead_slotwith the actual name:
This instantly frees all those WAL files. The disk space may take a minute to show free — check withSELECT pg_drop_replication_slot('dead_slot');df -h. - Set a safety limit. Edit
postgresql.confand add:
This stops any single slot from hogging more than 5GB of WAL. Reload withmax_slot_wal_keep_size = 5GBpg_ctl reloadorSELECT pg_reload_conf();. - Restart replication if needed. If the standby is coming back, re-create the slot after it's connected:
SELECT * FROM pg_create_physical_replication_slot('standby1');
What if dropping the slot doesn't free space?
Sometimes the WAL files are still referenced by a checkpoint. Run a manual checkpoint:
CHECKPOINT;Then check pg_wal size again. If it's still huge, your disk might have a separate WAL archive location. Check archive_command in postgresql.conf — if it writes to a directory that's also filling up, clear that too.Worst case: restart the PostgreSQL service. It triggers a clean checkpoint and releases stale WAL. But do this only during maintenance — it causes a brief outage.
How to stop this from happening again
Set max_slot_wal_keep_size to a sane value (like 5GB or 10GB) on every PostgreSQL 13+ instance. This is the simplest guard — it trims old WAL even if a slot is stuck.
Also monitor pg_replication_slots regularly. I use a simple cron job that alerts if any slot is inactive for more than 2 hours:
SELECT slot_name FROM pg_replication_slots WHERE NOT active AND pg_last_wal_receive_lsn() IS NOT NULL;If you're on PostgreSQL 12 or older, you don't have max_slot_wal_keep_size. Upgrade, or set wal_keep_segments to a low number (like 500) and rely on archive_command for backups.
One last thing: never leave a replication slot lying around after decommissioning a standby. Always drop the slot before removing the replica. It's the #1 cause of this mess.