max_wal_size

Maximum WAL size before checkpoint is forced. Too small triggers checkpoint storms: I/O spikes every few seconds, degrading write throughput.

HIGH RISK
Safe first move

1GB default (keep unless write-heavy: pg_stat_checkpointer.num_requested > num_timed)

Main risk

Too small (e.g. 64MB): 25 forced checkpoints in 60s (lab-captured), I/O spike every 2s, tps degraded. Too large: WAL disk usage grows between checkpoints (recovery time increases).

Tier 2 — Production Guide — Operational guidance is available; evidence scope is stated where relevant.

What We Observed

box

cl_wal_storm (--memory=1g, PG 17.10), pgbench TPC-B scale 5, -c 8 -j 4 -T 60

storm low

max wal size

64MB

tps

1569

latency avg ms

5.092

checkpoint events

25 requested vs 1 timed (storm ratio = 25:1)

storm messages

checkpoints are occurring too frequently (2 seconds apart)

source file

no storm tuned

max wal size

1GB (default)

tps

1474

latency avg ms

2.708

note

No storm messages. Checkpoints on timed schedule.

source file

historical curve

64MB tps

944

64MB storm warnings

24

128MB tps

965

128MB storm warnings

33

source

wal_curve.txt (2026-06-19 lab capture)

curve note

Checkpoint storm is threshold behavior: below critical WAL size → storm; above → stable.

What Was Expected

framing

Too small max_wal_size forces checkpoint every time WAL fills. Each checkpoint is I/O-intensive: all dirty buffers flushed to disk. Under write workload: repeated storms degrade tps, spike I/O, and create checkpointing overhead that competes with normal writes.

storm evidence
setting

max_wal_size=64MB, pgbench -c 8 -j 4 -T 60

container

cl_wal_storm (--memory=1g)

log lines

checkpoints are occurring too frequently (15s apart); (2s apart); (2s apart)

pg stat checkpointer

num_requested=25, num_timed=1 (storm: 25 forced checkpoints in 60s)

source file

historical evidence
log

2026-06-19: checkpoints occurring too frequently (6 seconds apart); (2 seconds apart); (1 second apart)

source

source file

Common Pitfalls

  • Treating max_wal_size as a HARD cap on WAL on disk. It is a soft checkpoint trigger; WAL can briefly exceed it under a spike, and replication slots / wal_keep_size can hold far more — out-of-disk WAL is a separate failure.
  • Leaving the 1GB default on a write-heavy server. Sustained high WAL rates blow through 1GB in seconds, firing volume-driven checkpoints; the only signal is 'checkpoints are occurring too frequently' in the log.
  • Tuning max_wal_size without checkpoint_timeout. Whichever trigger fires first wins; a huge max_wal_size still checkpoints every checkpoint_timeout, so they must be raised together.
  • Judging the setting by total WAL MB. Total MB is confounded by throughput (a storming server does less work); use the FPI fraction (wal_fpi/wal_records) and pg_stat_checkpointer.num_requested to see amplification and volume-triggering.
  • Oversizing 'as large as possible'. Past the time-driven crossover there is no throughput gain (plateau in the curve) and WAL can occupy more disk between checkpoints; target the crossover plus headroom.
  • Forgetting it is reload-only and cannot run in a transaction. ALTER SYSTEM SET must be its own statement (not combined with pg_reload_conf in one psql -c), then reload — no restart needed.
  • Ignoring wal_compression. With frequent checkpoints, full-page images dominate WAL; leaving compression off magnifies the write amplification the storm already causes.

Tuning Guidance

Unlock Incident Pro

The free sections tell you what to do. Pro is the measured proof — the exact failure signature, safe-ceiling formula, and per-workload defaults, each captured on real PG 14–18 runs.

  • Tuning Guidance
Unlock Incident Pro

max_wal_size is the SOFT upper bound on how much write-ahead log (WAL) may accumulate between automatic checkpoints. It is a trigger, not a hard cap: when WAL written since the last checkpoint approaches max_wal_size, PostgreSQL starts a checkpoint to flush dirty buffers so old WAL can be recycled. Checkpoints are therefore fired by whichever comes FIRST — time (checkpoint_timeout, default 5min) or volume (max_wal_size). Set it too small and the volume trigger dominates: checkpoints fire constantly, throughput collapses, and every checkpoint forces full-page images into the WAL (write amplification). Set it large enough and checkpoints become purely TIME-driven and spread out, which is almost always what you want on a write-heavy box. It is 'soft' because under a write spike WAL can briefly exceed it; the real hard limits are disk space and the separate wal_keep_size / replication-slot retention (covered elsewhere).

Measured literally from pg_settings on PG 14/15/16/17/18: setting=1024, unit=MB => effective default 1024MB (1GB) on ALL FIVE versions (boot_val=1024 AND reset_val=1024 everywhere — no auto-tuning, no version drift). vartype=integer, min_val=2 (2MB floor), max_val=2147483647 (MB => effectively unbounded). context='sighup' => changed with a config RELOAD, no restart and no session/role override (it is one cluster-wide value, unlike work_mem). default_changed=false and boot_fallback_changed=false across 14-18: this is one of the rare GUCs whose compiled fallback and effective default are identical and constant on every supported version. short_desc: 'Sets the WAL size that triggers a checkpoint.' Related fixed defaults observed: checkpoint_timeout=300s (5min), checkpoint_completion_target=0.9, min_wal_size=80MB, wal_compression=off.

References

  • PostgreSQL 18 docs — Write Ahead Log / Checkpoints: max_wal_size, min_wal_size, checkpoint_timeout, checkpoint_completion_target (postgresql.org/docs/18/runtime-config-wal.html)
  • PostgreSQL 18 docs — WAL Configuration & full-page writes / FPI amplification after checkpoints (postgresql.org/docs/18/wal-configuration.html)
  • PostgreSQL 18 docs — pg_stat_checkpointer (num_timed vs num_requested) and pg_stat_wal (wal_fpi, wal_records, wal_bytes) (postgresql.org/docs/18/monitoring-stats.html)
  • PostgreSQL 18 docs — pg_settings (boot_val vs reset_val semantics) (postgresql.org/docs/18/view-pg-settings.html)
  • Lab ground truth: truth/pg_settings_pg14.18.tsv (this repo)
  • Lab captures: wal_curve_hi.txt, wal_storm.txt} (this repo)

Technical Reference

Category
Write-Ahead Log / Checkpoints
Type
integer (MB)
Blast radius
cluster
Severity
High
Context
sighup
Versions
14, 15, 16, 17, 18
Reviewed
2026-06-19

Severity scale: 1 Minimal · 2 Low · 3 Moderate · 4 High · 5 Critical.

Keep going

Related & next steps

Parameters to check