Cookbook recipe

pg_wal Directory Filling Unexpectedly

Applies to PostgreSQL 13–17 Last reviewed May 2026 Grounded in source
Estimated investigation4 min

Scenario

Scenario The DBA receives a disk usage alert at 6 AM. The pg_wal directory has grown from its normal 1 GB to 40 GB overnight. The database is running fine — no errors. A check reveals…

Investigation Path

Scenario

The DBA receives a disk usage alert at 6 AM. The pg_wal directory has grown from its normal 1 GB to 40 GB overnight. The database is running fine — no errors. A check reveals a logical replication slot was created 3 days ago for a new subscriber that was never actually deployed. The slot is inactive (active = false) and has been holding all WAL generated since it was created.

How to Identify

Conditions:

  • pg_wal directory much larger than expected
  • pg_replication_slots shows slot with active = false and low restart_lsn
  • wal_status = 'reserved' — WAL is being held for the slot
  • pg_stat_archiver shows archiving failing (secondary cause)
  • wal_keep_size set very high

Analysis Steps

-- Check pg_wal size:
SELECT pg_size_pretty(sum(size)) AS wal_size, count(*) AS wal_files
FROM pg_ls_waldir();

-- Find slots holding WAL:
SELECT
    slot_name, slot_type, active, wal_status,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS held_wal
FROM pg_replication_slots
ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC NULLS LAST;

-- Check wal_keep_size (extra WAL retained beyond what slots need):
SHOW wal_keep_size;
SHOW max_wal_size;

-- Check archiver status (failing archive also retains WAL):
SELECT archived_count, failed_count, last_failed_wal, last_archived_wal
FROM pg_stat_archiver;

-- Calculate: how much WAL is held by each cause
SELECT
    current_setting('wal_keep_size') AS wal_keep_size,
    pg_size_pretty(sum(size)) AS total_pg_wal_size
FROM pg_ls_waldir();

Pitfalls

  • Inactive replication slots (active = false) hold WAL indefinitely until the slot is consumed or dropped. WAL accumulates with no size limit unless max_slot_wal_keep_size is set.
  • Dropping a slot immediately allows PostgreSQL to recycle WAL — but the slot’s subscriber will need to do a full table resync since the slot’s position is lost.
  • wal_keep_size retains WAL regardless of slots — both settings add to WAL retention.
  • Never delete files directly from pg_wal/ — PostgreSQL manages this directory exclusively.
  • max_slot_wal_keep_size (PG 13+) automatically invalidates slots that exceed their WAL budget, preventing unbounded growth.

Resolution Approach

Drop the inactive slot to immediately release held WAL. Set max_slot_wal_keep_size to prevent recurrence. Review wal_keep_size and reduce if it’s unnecessarily large.

This is a Pro lesson

Get every Learning Pathway and cookbook recipe — grounded in PostgreSQL source code, with diagnostics, fixes, and prevention for each topic.

Continue this lesson to learn:

  • Mitigation Actions
  • All 36 Learning Pathway lessons
  • 170+ cookbook recipes
  • Source-grounded diagnostics & fixes

Secure checkout Cancel anytime Source-grounded

Career Impact

This scenario builds production judgment and operational confidence under pressure.

Open Career Dashboard →

Keep going

Related & next steps

Was this helpful?

← All cookbook recipes