WAL and durability
The write-ahead log records every change before the data page is touched. fsync and synchronous_commit are the durability-vs-throughput knob; checkpoints bound recovery time; the same WAL powers replication and PITR.
A payments service commits an order, returns 200 OK to the customer, and the box loses power 80 milliseconds later. On reboot the order is there, total intact — even though the actual table page never made it to disk before the crash. That survival is not luck; it’s the write-ahead log doing exactly one job: writing down what you’re about to change before you change it. Flip one config flag — synchronous_commit = off — and that same order can vanish on the next crash, in exchange for 3× write throughput. This lesson is that flag and the machinery behind it.
Write-ahead: log first, then the page
Before you decide whether to flip synchronous_commit, you need to understand what it controls — because the two durability flags do very different things. The core rule of crash safety is write-ahead logging: before Postgres modifies a data page, it first appends a compact record of the change to the WAL — a sequential, append-only log. Only the WAL record must be flushed to durable storage at commit time; the actual table and index pages can be written later, lazily, by the background writer and checkpointer.
Why is this faster and safer than writing the data page itself at commit? Because WAL writes are sequential (append to the end of the current log file) while data-page writes are random (scattered across the heap). Forcing one small sequential fsync per commit is far cheaper than forcing many random page writes — yet it’s enough, because if the box crashes before the dirty pages are flushed, recovery replays the WAL and reconstructs them. The data pages are an optimization; the WAL is the source of truth.
-- Where WAL currently is, and how fast it's being generated:
SELECT pg_current_wal_lsn(); -- current write position (LSN)
SHOW synchronous_commit; -- on | off | remote_apply ...
SHOW wal_level; -- replica (default) | logical | minimal
SHOW max_wal_size; -- soft ceiling that triggers checkpointsThe durability/throughput knob
synchronous_commit controls when a commit is acknowledged relative to the WAL flush:
on(default) —COMMITwaits until the WAL record isfsync’d to durable storage. A committed transaction survives a crash. Full durability, one fsync of latency per commit.off—COMMITreturns immediately after writing WAL to the OS buffer, before fsync. The flush happens asynchronously withinwal_writer_delay(≈200 ms). On a crash you can lose the last fraction of a second of committed transactions — but the database stays consistent (no corruption, no torn state), you just lose the tail. In exchange, throughput on small write transactions can jump 2–3× because commits no longer block on disk.
This is the single most consequential durability tuning decision. The senior framing: synchronous_commit = off is not “unsafe” the way disabling fsync is — it never corrupts the database, it only widens the window of lost-but-acknowledged commits. For a payments ledger: never. For an analytics ingest pipeline that can replay its source: often a free 3× win.
Two related flags people confuse: fsync = off is the genuinely dangerous one — it lets the WAL itself skip the disk flush, so a crash can corrupt the cluster; never use it outside a throwaway test box. And synchronous_commit = remote_apply extends durability to a replica having applied the change (covered with replication below).
Checkpoints bound recovery
If WAL accumulated forever, crash recovery would replay from the dawn of time. A checkpoint is the periodic act of flushing all dirty data pages to the heap and recording, “everything up to this WAL position is now safely on the data files.” Recovery only needs to replay WAL after the last checkpoint — so checkpoint frequency bounds recovery time.
The tradeoff: frequent checkpoints mean short recovery but more constant write I/O (flushing dirty pages often); rare checkpoints mean less steady I/O but a long, spiky recovery and large WAL. Postgres triggers a checkpoint when either checkpoint_timeout (default 5 min) elapses or WAL hits max_wal_size (default 1 GB). Production tuning usually raises both so checkpoints are spread out and write spikes smooth, accepting a longer recovery — checkpoint_completion_target (default 0.9) spreads the flush across the interval to avoid an I/O wall.
▸Why this works
Why is the same WAL the basis of both replication and point-in-time recovery? Because the WAL is a complete, ordered description of every change to the cluster. Streaming replication ships that WAL stream to a standby, which replays it continuously to stay a near-live copy — that’s how read replicas and high-availability failover work. Point-in-time recovery (PITR) takes a base backup plus archived WAL segments and replays WAL up to any chosen timestamp or LSN, so you can restore to “5 seconds before the bad migration ran.” Both are just “replay the WAL somewhere else” — which is exactly what crash recovery does locally. Set wal_level = replica (the default) or logical to keep enough WAL detail for these; minimal logs less and forfeits them.
Why does logging the change to WAL before writing the data page make commits both fast and durable?
What exactly happens with synchronous_commit = off if the server crashes right after a COMMIT returned?
Fill in the blank: a write-ahead log records the change _______ the data page is modified, so a crash can be repaired by replaying the log to reconstruct any page that wasn't yet flushed.
- 01What is write-ahead logging and why is it faster and safer than writing the data page at commit?
- 02What does synchronous_commit = off trade, and how is it different from fsync = off?
- 03What is a checkpoint and how does it relate to crash recovery and replication?
Write-ahead logging is Postgres’s durability mechanism: every change is appended to the sequential WAL before the data page is modified, and at commit only that small WAL record is fsync’d — the heap and index pages flush lazily, because a crash is repaired by replaying the WAL from the last checkpoint. That’s why commits are fast (one sequential fsync, not many random page writes) and durable (the WAL is the source of truth). The key knob is synchronous_commit: on waits for the WAL fsync (full durability); off acknowledges before it, risking the last sub-second of committed transactions on a crash while keeping the database consistent and uncorrupted, for a 2–3× throughput gain — categorically safer than the never-in-prod fsync = off. Checkpoints flush dirty pages and cap how much WAL recovery must replay; tune checkpoint_timeout, max_wal_size, and checkpoint_completion_target to spread write I/O against recovery time. Finally, the very same WAL is what powers streaming replication (ship and replay on a standby) and PITR (replay to a chosen timestamp) — recovery, replication, and time-travel restore are all just “replay the log.” Now when you see a database that survived a crash intact, or a replica that’s a second behind primary, or a restore to a point in time — you know which single mechanism made all three possible.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.
Apply this
Put this lesson to work on a real build.