PostgreSQL Checkpoints: Dirty Pages, WAL, and Recovery Performance

Jun 18, 2026 · 11 min read

Learn how PostgreSQL checkpoints work, why dirty pages must be flushed to disk, and how checkpoints affect WAL, recovery time, and performance.

PostgreSQL Checkpoints: Dirty Pages, WAL, and Recovery Performance

After learning about WAL, I realized there’s still an important piece missing from the durability story. If PostgreSQL can recover changes from WAL, why does it bother writing modified data pages to disk at all? The answer leads directly to checkpoints, one of the most important background processes in PostgreSQL.

Why Checkpoints Exist

In the previous article, I learned that PostgreSQL doesn’t immediately write modified table pages to disk.

When a transaction updates data, the modified page remains in memory inside shared_buffers. PostgreSQL refers to these modified pages as dirty pages.

UPDATE accounts
SET balance = balance - 100
WHERE id = 1;

After this statement:

  • WAL records are generated
  • WAL is flushed to disk
  • Transaction commits
  • Dirty pages remain in memory

The actual table page may not be written to disk for quite some time.

This works because WAL guarantees durability. However, if PostgreSQL never flushed dirty pages, crash recovery would eventually require replaying an enormous amount of WAL.

Checkpoints exist to limit how much WAL PostgreSQL must replay during recovery.

Dirty Pages and WAL Relationship

A checkpoint acts as a synchronization point between memory, WAL, and disk storage.

What Happens During a Checkpoint

A checkpoint is essentially PostgreSQL saying:

“Let’s make sure all important dirty pages are safely stored on disk.”

When a checkpoint starts, PostgreSQL begins flushing dirty pages from shared_buffers into data files.

The process looks like:

  1. Dirty pages exist in memory
  2. Checkpoint begins
  3. Dirty pages are written to disk
  4. Checkpoint record is written into WAL
  5. PostgreSQL marks a new recovery starting point

Internally, PostgreSQL writes a special checkpoint record into the WAL stream.

If a crash occurs later, PostgreSQL can start recovery from this checkpoint instead of replaying WAL from the beginning of time.

Checkpoint Execution Flow

Without checkpoints, crash recovery time would continuously grow as more WAL accumulates.

How Checkpoints Work Together with WAL

One thing that confused me initially was whether checkpoints replace WAL.

They don’t.

WAL and checkpoints solve different problems.

WAL provides durability.

Checkpoints reduce recovery time.

Imagine this timeline:

Checkpoint A
→ WAL Records
→ WAL Records
→ WAL Records
→ Crash

When PostgreSQL restarts:

  • It locates Checkpoint A
  • Reads WAL generated after Checkpoint A
  • Replays only those changes
  • Restores consistency

Everything before Checkpoint A is already guaranteed to exist in data files.

That’s why recovery doesn’t need to replay older WAL records.

Checkpoints don’t eliminate WAL. They simply reduce the amount of WAL that needs to be replayed.

Checkpoint and WAL Recovery Window

This relationship is one of the most important concepts in PostgreSQL internals.

How PostgreSQL Decides When to Run a Checkpoint

PostgreSQL does not run checkpoints after every transaction.

That would generate excessive disk I/O.

Instead, checkpoints are triggered by configurable thresholds.

Common settings include:

checkpoint_timeout = 5min
max_wal_size = 1GB

A checkpoint occurs when either:

  • checkpoint_timeout is reached
  • WAL generation exceeds max_wal_size
  • A manual checkpoint is requested

You can trigger one manually:

CHECKPOINT;

And inspect checkpoint statistics:

SELECT *
FROM pg_stat_bgwriter;

Modern PostgreSQL versions also expose:

SELECT *
FROM pg_stat_checkpointer;

The goal is to balance runtime performance with recovery speed.

Checkpoint Trigger Conditions

Larger intervals reduce I/O pressure but increase recovery work after a crash.

Understanding Checkpoint Pressure

Checkpoint pressure is a term often used when PostgreSQL struggles to flush dirty pages quickly enough.

Imagine a busy application:

  • Thousands of updates per second
  • Large numbers of dirty pages
  • Rapid WAL generation

If checkpoints cannot keep up, PostgreSQL may suddenly need to flush a huge amount of data.

This can create:

  • Increased disk I/O
  • Higher query latency
  • Write stalls
  • Throughput drops

Checkpoint pressure is especially noticeable on slower storage systems.

A useful query:

SELECT
    checkpoints_timed,
    checkpoints_req,
    checkpoint_write_time,
    checkpoint_sync_time
FROM pg_stat_bgwriter;

High write times often indicate the storage subsystem is struggling during checkpoint activity.

Many PostgreSQL performance issues blamed on checkpoints are actually storage bottlenecks becoming visible during checkpoint writes.

Understanding this distinction is important when diagnosing production systems.

Tuning Checkpoints for Production

Checkpoint tuning is largely about controlling how aggressively PostgreSQL writes dirty pages.

Common settings:

checkpoint_timeout = 15min
max_wal_size = 4GB
checkpoint_completion_target = 0.9

One setting I found particularly interesting is:

checkpoint_completion_target = 0.9

This tells PostgreSQL to spread checkpoint writes across roughly 90% of the checkpoint interval.

Instead of writing everything at once, PostgreSQL distributes writes gradually.

Benefits include:

  • Reduced I/O spikes
  • Smoother latency
  • More predictable performance

The tradeoff is that larger checkpoint intervals require more WAL retention and slightly longer crash recovery times.

Checkpoint Tuning Strategy

Like most PostgreSQL tuning decisions, the right values depend heavily on workload characteristics and storage performance.

Final Thoughts

Checkpoints are the bridge connecting memory, WAL, and persistent storage. WAL guarantees durability by recording changes before data pages reach disk, while checkpoints ensure those dirty pages eventually become permanent.

The key idea is simple: WAL protects committed transactions, and checkpoints reduce recovery work. Together they allow PostgreSQL to provide durability without constantly flushing every write directly to disk.

After learning about WAL and checkpoints together, PostgreSQL’s recovery model feels much easier to understand. Recovery, replication, durability, dirty pages, and WAL retention all revolve around the relationship between these two mechanisms.

PostgreSQL Database Internals WAL Performance
Available for opportunities

Need someone who enjoys solving hard engineering problems?

I enjoy building reliable systems, understanding complex architectures, and solving challenging engineering problems. If you're looking to hire a software engineer who combines curiosity, ownership, and strong technical fundamentals, I'd love to hear from you.

deploy-status

> Status: Available

> Curiosity: HIGH

> Auto Scaling: Enabled