Skip to main content
RunBook Academy

PostgreSQLXII · WAL, Checkpoints and Crash RecoveryWAL

Crash recovery: what "consistent state" means

Intermediate⏱ ~30 minpsqlpg_controldata

What you'll learn

  • Read a crash recovery log and say what each line means
  • Estimate recovery time from checkpoint distance and replay rate
  • Distinguish a normal recovery from one that is genuinely failing
  • Use pg_controldata to establish a stopped cluster's state

Prerequisites

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.

Crash recovery is not an exceptional path. It is the same code that runs on every standby continuously, and it runs on the primary any time the cluster was not shut down cleanly.

What it does

  1. Read pg_control to find the last checkpoint’s redo location.
  2. Replay every WAL record from there forward.
  3. Stop when a record is not valid — that is the end of the log.
  4. Take a checkpoint.
  5. Accept connections.

Committed transactions are replayed and become visible. Uncommitted ones were never visible and remain so. Nothing is rolled back, because nothing needs to be: from lesson VII-01, an aborted transaction’s versions are simply never visible to anyone.

The log, line by line

Service impact possiblea real crash recovery
$ docker kill --signal=KILL rbpg-stor && docker start rbpg-stor
LOG:  database system was interrupted; last known up at 2026-08-27 21:01:01 UTC
LOG:  database system was not properly shut down; automatic recovery in progress
LOG:  redo starts at 7/CB35CEA8
LOG:  invalid record length at 7/CF4461A0: expected at least 24, got 0
LOG:  redo done at 7/CF446178 system usage: CPU: user: 0.09 s, system: 0.03 s, elapsed: 0.12 s
LOG:  checkpoint starting: end-of-recovery immediate wait
LOG:  checkpoint complete: wrote 6003 buffers (36.6%), wrote 2 SLRU buffers; 0 WAL file(s) added, 4 removed, 0 recycled; write=0.026 s, sync=0.018 s, total=0.066 s; sync files=9, longest=0.013 s, average=0.002 s; distance=66468 kB, estimate=66468 kB; lsn=7/CF4461A0, redo lsn=7/CF4461A0
LOG:  database system is ready to accept connections
Read-only / Safethe data, afterwards
$ psql -U postgres -At -c "SELECT count(*) FROM crashtest"
300000
LineMeaning
was interrupted; last known up atThe control file was not marked shut down
was not properly shut down; automatic recovery in progressRecovery is starting
redo starts at 7/CB35CEA8The last checkpoint’s redo location
invalid record length … got 0The end of the WAL. Normal.
redo done at 7/CF446178 … elapsed: 0.12 sReplay finished, with timing
checkpoint starting: end-of-recovery immediate waitEstablishing a new recovery point
database system is ready to accept connectionsDone

Estimating recovery time

Measured here: 65 MB replayed in 0.12 seconds of elapsed time. That is this hardware, warm cache, a simple insert workload — but the method generalises:

recovery time ≈ WAL to replay ÷ replay rate
WAL to replay ≈ up to max_wal_size, or checkpoint_timeout × generation rate

pg_controldata

The authoritative state of a cluster that is not running.

Read-only / Safepg_controldata on a healthy running cluster
$ pg_controldata "$PGDATA"
pg_control version number:            1800
Catalog version number:               202506291
Database system identifier:           7678780827444375601
Database cluster state:               in production
pg_control last modified:             Thu 27 Aug 2026 09:01:28 PM UTC
Latest checkpoint location:           7/D327F4D8
Latest checkpoint's REDO location:    7/D327F4D8
Latest checkpoint's REDO WAL file:    0000000100000007000000D3
Latest checkpoint's TimeLineID:       1
Latest checkpoint's PrevTimeLineID:   1
Latest checkpoint's full_page_writes: on
Latest checkpoint's NextXID:          0:848603
Latest checkpoint's NextOID:          27921
Latest checkpoint's NextMultiXactId:  4
FieldUse
Database cluster stateshut down is clean; in production on a stopped cluster means it crashed
Latest checkpoint's REDO locationWhere recovery will start
Latest checkpoint's REDO WAL fileThe earliest segment that must exist
Database system identifierIdentifies the cluster lineage. Must match between a primary and its standbys
Latest checkpoint's TimeLineIDWhich timeline. Relevant after any promotion

Latest checkpoint's REDO WAL file is the precise answer to “which WAL files do I still need” for crash recovery — that file and everything after it. It is not the answer for archiving or replication, which retain from their own positions.

Clean shutdown, for contrast

Read-only / Safethe same cluster after docker stop, which sends SIGTERM
$ docker stop rbpg-stor && docker start rbpg-stor
LOG:  starting PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) on x86_64-pc-linux-gnu
LOG:  listening on IPv4 address "0.0.0.0", port 5432
LOG:  database system was shut down at 2026-08-27 21:01:27 UTC
LOG:  database system is ready to accept connections

database system was shut down at versus database system was interrupted; last known up at is how you determine, after the fact, whether the last stop was clean. It is the first line to look for in a post-incident log review.

What to take from this

  • Recovery replays from the last checkpoint’s redo location to the end of the WAL, then checkpoints.
  • invalid record length … got 0 at LOG level before redo done is the normal end marker, not corruption.
  • Measured: 300,000 rows survived a SIGKILL; 65 MB replayed in 0.12 s.
  • Measure your own replay rate on a copy, and make the max_wal_size decision against it.
  • pg_controldata’s cluster state and REDO WAL file are the first two fields to read on a stopped cluster.
  • Replay is serial. More cores do not help; storage latency and recovery_prefetch do.

Cross-course references

  • Linux for Production Sysadmins — Part VIII (Logging and journald) covers capturing the redo log lines before they rotate, and Part LXXXII (Root cause analysis) covers establishing what caused the crash rather than being satisfied that recovery worked.
  • Docker & Containers — Part VI (Container lifecycle) covers the stop-then-kill sequence that makes a crash the normal case for a containerised cluster.

Quiz

Knowledge check · 6 questions

  1. Q1. A standby is 40 GB behind in replay and the primary has failed. What dominates the time to bring the standby into service?

  2. Q2. A cluster that would not start shows 'Database cluster state: in production' in pg_controldata. What does that tell you?

  3. Q3. Which of these log lines during recovery indicates a genuine failure rather than normal completion?

  4. Q4. Which factors make WAL replay slower than the original write path? Select all that apply.

  5. Q5. Crash recovery must roll back uncommitted transactions before the cluster can accept connections.

  6. Q6. How would you establish what a crash costs your cluster in recovery time, and what decision does that number inform?

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