Skip to main content
RunBook Academy

PostgreSQLXII · WAL, Checkpoints and Crash RecoveryWAL

WAL segments, LSNs and generation rate

Intermediate⏱ ~25 min🧪 Lab requiredpsql

What you'll learn

  • Measure a cluster's WAL generation rate and project it
  • Map an LSN to a segment file and back
  • Read pg_stat_wal and act on what it reports
  • Identify which workload changes move the WAL rate most

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.

WAL is a single logical stream written into fixed-size files. Knowing how fast yours grows is a prerequisite for sizing archives, replication links, and the filesystem pg_wal lives on.

Segments

The stream is divided into segments of wal_segment_size, 16 MB by default and fixed at initdb time. A segment is named from three 8-character hexadecimal fields:

0000000100000007000000CF
^^^^^^^^                  timeline
        ^^^^^^^^          high 32 bits of the LSN
                ^^^^^^^^  segment number within that

The mapping is available directly rather than by hand:

Read-only / SafeLSN to segment file
$ psql -U postgres -c "SELECT pg_walfile_name('7/CF446178')"
 pg_walfile_name
--------------------------
0000000100000007000000CF

Measuring the rate

Read-only / SafeWAL generated by a measured workload
$ psql -U postgres -At -c "SELECT pg_current_wal_lsn()"   # before and after the workload
tps = 2218.150222

start_lsn   |  end_lsn   | wal_in_20s | wal_per_second
-------------+------------+------------+----------------
7/C0F3DB58  | 7/C645F2A0 | 85 MB      | 4359 kB

4,359 kB per second. Projected:

PeriodWAL
Per minute~255 MB
Per hour~15 GB
Per day~367 GB

That is the number every downstream capacity question depends on:

  • pg_wal filesystem size must hold max_wal_size plus whatever slots and archiving retain.
  • Archive storage grows at this rate, before compression.
  • Replication bandwidth must sustain it, or replicas fall behind permanently.
  • Recovery time is this rate multiplied by the checkpoint distance.

The measurement takes twenty seconds. Do it on the real workload rather than on pgbench, and do it again after any significant schema change.

-- a reusable form: run twice, a minute apart
SELECT now(), pg_current_wal_lsn();

pg_stat_wal

Read-only / Safepg_stat_wal after about two and a half hours of mixed load
$ psql -U postgres -x -c "SELECT * FROM pg_stat_wal"
wal_records      | 82823247
wal_fpi          | 3150464
wal_bytes        | 33429991279
wal_buffers_full | 2554998
stats_reset      | 2026-08-27 18:31:22.55921+00
ColumnReading
wal_recordsTotal records written
wal_fpiFull page images. Against wal_records, how much of your WAL is torn-page protection
wal_bytesTotal bytes. With stats_reset, gives the rate
wal_buffers_fullTimes a backend had to wait because the WAL buffer was full

What moves the rate

In rough order of how much difference each makes:

Non-HOT updates. Lesson VII-02 measured 2.8× more WAL from fillfactor alone, and 2.6× from adding one index to an updated column. This is usually the largest available reduction.

Index count. Lesson X-05 measured 3.6× more WAL for seven indexes against one, on identical inserts.

Checkpoint frequency. Every checkpoint restarts the full-page-write cycle. Lesson XII-01 measured 50% more WAL in the round following one.

Freezing. Lesson VIII-05 measured a first freeze generating roughly the table’s own size in WAL.

wal_compression. Compresses full page images specifically. On a workload where wal_fpi is a large share of bytes, this is straightforwardly worthwhile.

Batch size. Ten thousand single-row transactions generate substantially more WAL than one transaction inserting ten thousand rows, because each commit writes its own record and each may force a flush.

What to take from this

  • Segments are 16 MB by default and fixed at initdb. The name encodes timeline and LSN.
  • Measured: 2,218 tps produced 4,359 kB/s, about 367 GB a day. Measure yours; it takes twenty seconds.
  • wal_buffers_full growing means wal_buffers is too small. It is cheap shared memory and needs a restart.
  • wal_fpi against wal_records says how much of your WAL is torn-page protection.
  • The largest reductions available are usually HOT updates and index count, not WAL settings.
  • max_wal_size is a soft target. Steady-state pg_wal is larger, and slots and archiving are not bounded by it at all.

Cross-course references

  • Observability for Production Sysadmins — Part XIII (Rates and counters) covers turning an LSN into a generation rate, which is the input to archive sizing and to replication bandwidth.
  • Linux for Production Sysadmins — Part XLI (Storage Performance) covers whether the device can absorb that rate alongside everything else on it.

Quiz

Knowledge check · 6 questions

  1. Q1. pg_wal on a cluster with max_wal_size set to 1 GB is consistently around 2.2 GB, with no replication slots and a healthy archiver. Is this a problem?

  2. Q2. pg_stat_wal shows wal_buffers_full climbing into the millions on a write-heavy cluster. What does it mean and what is the action?

  3. Q3. A team wants to reduce WAL volume on a cluster generating 367 GB a day. Which change is likely to give the largest reduction?

  4. Q4. Which of these can make pg_wal grow far beyond max_wal_size? Select all that apply.

  5. Q5. wal_segment_size can be raised on a running cluster with a configuration reload to reduce the number of archive operations.

  6. Q6. How would you measure a production cluster's WAL generation rate, and what does the number let you plan?

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