Skip to main content
RunBook Academy

PostgreSQLXII · WAL, Checkpoints and Crash RecoveryWAL

Checkpoints: what they do and what they cost

Advanced⏱ ~30 min🧪 Lab requiredpsql

What you'll learn

  • Explain what a checkpoint does and what it makes possible
  • Read the checkpoint log line and pg_stat_checkpointer
  • Diagnose checkpoints that are too frequent from evidence
  • Balance checkpoint frequency against recovery time deliberately

Prerequisites

Practice

Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27

Not yet marked complete on this device.

A checkpoint writes every dirty buffer to disk and records the WAL position at which it started. Recovery can then begin from that position instead of from the beginning of time, and every WAL segment before it can be recycled.

Both of those consequences matter, and they pull in opposite directions.

What triggers one

TriggerSettingReported as
Time elapsedcheckpoint_timeout (300 s)num_timed
WAL volumemax_wal_size (1 GB)num_requested
CHECKPOINT commandnum_requested
Clean shutdownnum_requested
End of recoverynum_requested

The ratio that diagnoses the cluster

Read-only / Safepg_stat_checkpointer after two and a half hours of mixed load
$ psql -U postgres -x -c "SELECT * FROM pg_stat_checkpointer"
num_timed           | 26
num_requested       | 54
num_done            | 62
restartpoints_timed | 0
restartpoints_req   | 0
restartpoints_done  | 0
write_time          | 2205951
sync_time           | 9173
buffers_written     | 210247
slru_written        | 126

54 requested against 26 timed. More than twice as many checkpoints were forced by WAL volume as occurred on schedule.

A timed checkpoint is spread over checkpoint_completion_target × checkpoint_timeout — 270 seconds on defaults — so its writes are gentle. A requested one arrives when max_wal_size is reached, which on a busy cluster is far sooner, and the spreading window shrinks with it.

The consequences of frequent forced checkpoints:

More full page writes. Lesson XII-01 measured 50% more WAL in the round after a checkpoint. Doubling the checkpoint rate applies that penalty twice as often, which generates more WAL, which triggers checkpoints sooner. It is a genuine feedback loop.

Less write coalescing. A page dirtied ten times between checkpoints is written once. A page dirtied ten times across five checkpoints is written five times.

Sharper I/O spikes, because each checkpoint has less time to spread its writes.

Reading the log line

ALTER SYSTEM SET log_checkpoints = on;   -- on by default since 15
Read-only / Safetwo real checkpoint completions from this cluster
$ grep 'checkpoint complete' /var/log/postgresql/postgresql.log
LOG:  checkpoint complete: wrote 2790 buffers (17.0%), wrote 3 SLRU buffers;
    0 WAL file(s) added, 0 removed, 33 recycled;
    write=269.303 s, sync=0.011 s, total=269.392 s;
    sync files=42, longest=0.005 s, average=0.001 s;
    distance=536114 kB, estimate=536114 kB;
    lsn=6/D6CE9F60, redo lsn=6/C8031640

LOG:  checkpoint complete: wrote 11585 buffers (70.7%), wrote 1 SLRU buffers;
    0 WAL file(s) added, 0 removed, 25 recycled;
    write=46.400 s, sync=0.020 s, total=46.476 s;
    distance=410246 kB, estimate=577182 kB
FieldReading
wrote N buffers (P%)How much of the pool was dirty. Above ~50% suggests the background writer is not keeping up
write=Time spreading the writes. Should approach completion_target × timeout
sync=Time in fsync. This is the number that indicates storage trouble
distance=WAL generated since the last checkpoint
estimate=Smoothed prediction used to size recycling
recycled / removedSegments renamed forward for reuse, and deleted
redo lsn=Where recovery would start from

write=269.303 s against a 300-second timeout is 0.898 — exactly checkpoint_completion_target. The writes were deliberately spread, and this is what a healthy checkpoint looks like.

sync=0.011 s against write=269.303 s. Almost all of a healthy checkpoint is spreading, not syncing. When sync_time becomes a significant fraction, storage is the constraint and no checkpoint setting will fix it.

The second line is the contrast: write=46.400 s and 70.7% of the pool dirty. That is a forced checkpoint, arriving early with a large backlog and less time to place it.

PostgreSQL 18 changed the log line format slightly, reporting shared buffers and SLRU buffers separately. From the release notes:

Add column pg_stat_checkpointer.slru_written to report SLRU buffers written … Also, modify the checkpoint server log message to report separate shared buffer and SLRU buffer values.

Alerting or log parsing written against the pre-18 format needs adjusting.

The trade, stated

Frequent checkpointsInfrequent checkpoints
WAL volumeHigher (full page writes)Lower
I/O smoothnessSpikierSmoother
Write coalescingWorseBetter
Recovery timeShorterLonger
pg_wal sizeSmallerLarger

Everything except recovery time favours infrequent checkpoints. The usual settings that follow:

ALTER SYSTEM SET checkpoint_timeout = '15min';
ALTER SYSTEM SET max_wal_size = '16GB';
ALTER SYSTEM SET checkpoint_completion_target = '0.9';  -- default since 14
ALTER SYSTEM SET min_wal_size = '2GB';

What to take from this

  • num_requested exceeding num_timed means max_wal_size is too small. Measured here at 54 against 26.
  • Size max_wal_size so a full checkpoint_timeout of WAL fits, from the measured generation rate.
  • The cost of infrequent checkpoints is recovery time. Calculate it and agree it as a service level.
  • write= should approach completion_target × timeout. sync= becoming significant means storage is the constraint.
  • CHECKPOINT by hand is unspread and heavy, and does not release WAL held by a slot or the archiver.
  • On a standby, recovery distance is set by the primary’s checkpoint frequency.

Cross-course references

  • Linux for Production Sysadmins — Part XLI (Storage Performance) covers measuring the write burst a checkpoint produces, and Part XL (Memory Performance) covers the page cache it flushes through.
  • Observability for Production Sysadmins — Part XIII (Rates and counters) covers the timed-versus-requested checkpoint ratio, which is the number that says whether max_wal_size is sized correctly.

Quiz

Knowledge check · 6 questions

  1. Q1. pg_stat_checkpointer shows num_timed 26 and num_requested 54. What does this indicate?

  2. Q2. A checkpoint log line reports write=269.303 s and sync=0.011 s on a cluster with checkpoint_timeout of 300 s. How should this be read?

  3. Q3. A promoted standby took far longer to become available than the primary's own crash recovery would have. Its checkpoint_timeout was set to 2 minutes. Why did that not help?

  4. Q4. Which are consequences of making checkpoints less frequent? Select all that apply.

  5. Q5. Issuing CHECKPOINT by hand is an effective response to a pg_wal directory that is filling up.

  6. Q6. How would you choose max_wal_size for a cluster, and what must be agreed before you set it?

Passing score: 75%. Answers are checked in this browser.