PostgreSQL Checkpoints Are Latency Events, Not Maintenance Events

Sep 1

A checkpoint is usually described as a recovery mechanism. Operationally, it is also a latency event.

PostgreSQL must eventually make dirty heap and index pages durable. At a checkpoint it guarantees that pages preceding the checkpoint record have reached their data files. That bounds crash recovery, but it also creates coordinated write pressure across the database, operating system, and storage device.

The useful question is not “how often should checkpoints run?” It is:

Can the storage system absorb checkpoint work without stealing the latency budget from foreground transactions?

The hidden feedback loop

PostgreSQL begins a checkpoint because checkpoint_timeout elapsed or WAL is approaching max_wal_size. The checkpointer spreads writes toward checkpoint_completion_target; current PostgreSQL defaults aim to use most of the interval instead of emitting one burst.

That pacing can still break down:

write burst
  → WAL grows quickly
  → requested checkpoint starts early
  → dirty pages compete for I/O
  → commits and reads slow
  → application requests overlap longer
  → concurrency and queueing increase

This is why average disk utilization is a weak signal. A device can look comfortable over a minute while millisecond-scale fsync latency destroys the database’s p99.

Full-page writes change the economics

With full_page_writes enabled, PostgreSQL logs a full page on its first modification after each checkpoint. This protects against torn pages. It also means overly frequent checkpoints can increase WAL volume, which can trigger more checkpoints. Reducing checkpoint_timeout may shorten recovery while increasing steady-state write amplification.

Treat the settings as one policy:

ControlBenefitCost
Larger max_wal_sizefewer requested checkpointsmore disk headroom and potentially longer recovery
Longer timeoutfewer full-page-write cyclespotentially more WAL to replay
High completion targetsmoother writesless slack if storage falls behind
Faster storagelower flush latencycost does not repair poor capacity assumptions

max_wal_size is a soft limit, not a disk quota. Archiving failures and replication slots can retain WAL independently. Capacity planning must include those failure states.

Observe the mechanism

Start with pg_stat_checkpointer and correlate it with host telemetry:

select checkpoints_timed,
       checkpoints_req,
       checkpoint_write_time,
       checkpoint_sync_time,
       buffers_written
from pg_stat_checkpointer;

Then compare counter deltas against WAL bytes, device latency, application p95/p99, replication lag, and batch activity. A growing requested-to-timed checkpoint ratio points toward WAL pressure. Long sync time points toward the durability path. Neither is explained by CPU alone.

Production policy

  1. Reserve disk for WAL retention failures, not only normal volume.
  2. Alert on checkpoint rate and duration, not merely disk percentage.
  3. Rate-limit bulk imports and index builds against an I/O budget.
  4. Run crash-recovery tests before trading recovery time for throughput.
  5. Change one control at a time and compare counter deltas over representative peaks.

Do not disable durability controls to make a benchmark green. If checkpoints expose a weak storage path, removing the evidence does not remove the constraint.

A useful capacity exercise

Estimate peak WAL generation in bytes per second, not transactions per second. Then model how much dirty data the device must flush across the checkpoint interval while still serving foreground reads, WAL syncs, autovacuum, and replication. Test the model with the same storage class used in production; local SSD results say little about network-attached volumes.

Include a failure case in which archiving stalls or a replication slot stops advancing. The test should answer two independent questions: how long until latency becomes unacceptable, and how long until retained WAL consumes the reserved disk. Those are different clocks and need different alerts.

Conclusion

A checkpoint connects recovery objectives to live request latency. The correct configuration is therefore workload-specific: WAL rate, dirty-page rate, device behavior, recovery target, replicas, and archiving all participate.

Design the checkpoint budget before the first write-heavy incident. PostgreSQL will always pay the durability bill; architecture decides whether it is paced, observable, and affordable.


Further reading: PostgreSQL WAL configuration, PostgreSQL monitoring statistics, Vacuum is concurrency control, and connections are a capacity budget.

>