PostgreSQLVI · Storage, Pages and TOASTStorage
The durability chain from COMMIT to platter
What you'll learn
- Trace the path from COMMIT to durable storage and name each layer
- State what fsync, synchronous_commit and full_page_writes each protect against
- Identify which layers can acknowledge a write before it is durable
- Reason about a storage platform's durability claims without accepting them
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
When COMMIT returns without an error, PostgreSQL has promised that
the transaction survives a crash. Understanding exactly what it did to
earn that promise — and what it is relying on others to have done — is
the foundation of every durability decision in this course.
The chain
flowchart TD
A["COMMIT issued"] --> B["WAL record written\ninto the WAL buffers"]
B --> C["write() to the WAL file\nnow in the OS page cache"]
C --> D["fsync / fdatasync\nOS asked to make it durable"]
D --> E["Device receives the flush"]
E --> F["Data is on stable media"]
F --> G["COMMIT returns to the client"]
H["The dirty data PAGE\nis still only in shared_buffers"] -.-> I["Written later by the\ncheckpointer"]
The critical structural fact is on the right-hand branch. COMMIT
does not write the data page. It makes the log record durable. The
page is written later, and if the server crashes before that happens,
recovery replays the log to reconstruct it. Part XII covers the replay;
this lesson is about the flush.
The three parameters that govern it
$ psql -U postgres -c "SELECT name, setting, context FROM pg_settings WHERE name IN ('fsync','synchronous_commit','full_page_writes','wal_sync_method') ORDER BY name" name | setting | context
--------------------+-----------+---------
fsync | on | sighup
full_page_writes | on | sighup
synchronous_commit | on | user
wal_sync_method | fdatasync | sighup
(4 rows)fsync decides whether PostgreSQL asks the operating system to
make writes durable at all. With it off, PostgreSQL never issues a
flush, and a crash of the host — not the database — can lose or
corrupt arbitrary data.
synchronous_commit decides whether COMMIT waits for that
flush. With it off, PostgreSQL still flushes, but the client is told
the transaction committed before the flush completes.
full_page_writes decides whether the first modification of a page
after a checkpoint writes the whole page into WAL, protecting against
torn pages.
The difference between the first two matters enormously and is routinely conflated.
fsync = off | synchronous_commit = off | |
|---|---|---|
| Flush issued? | No | Yes, just later |
| Lose recent commits on OS crash? | Yes | Yes, a bounded window |
| Database left corrupt after OS crash? | Yes, possibly | No |
| Reasonable in production? | Never | Sometimes, deliberately |
What each layer can lie about
PostgreSQL issues fsync() and trusts the answer. Every layer below
can return success before the data is on stable media.
| Layer | How it can acknowledge early | Detectable by PostgreSQL? |
|---|---|---|
| OS page cache | Only if fsync is off, or the call is ignored | No |
| Filesystem | Journal modes, barrier settings | No |
| Volume manager / RAID | Write-back cache without a battery | No |
| Virtualisation | Host cache mode set to writeback/unsafe | No |
| Network storage | Acknowledging before the remote commit | No |
| The disk itself | Volatile write cache, no power-loss protection | No |
Every row in that “detectable” column says no, and that is the whole problem. PostgreSQL’s durability is exactly as good as the weakest honest layer beneath it, and it has no way to measure that.
Verifying what you can from inside
Some of the chain is visible from the host, and it is worth checking the parts that are.
# What PostgreSQL believes it is doing
psql -U postgres -c \
"SELECT name, setting FROM pg_settings
WHERE name IN ('fsync','synchronous_commit','full_page_writes','wal_sync_method')"
# Which flush method is actually available and chosen
psql -U postgres -c 'SHOW wal_sync_method'
# Filesystem mount options: barriers, journal mode
findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS "$(psql -U postgres -tAc 'SHOW data_directory')"
# Device write cache, where the device exposes it
lsblk -o NAME,ROTA,SCHED,MODEL 2>/dev/null || true
wal_sync_method is worth a look. PostgreSQL selects a default
appropriate to the platform — fdatasync on Linux — and the
alternatives differ in how many system calls they make and whether they
flush metadata as well as data. Changing it is a measurement exercise
rather than a default worth adjusting.
Production discipline
- Never set
fsync = offon anything whose data matters. It is not a performance setting; it removes the guarantee that makes recovery possible. - Use
synchronous_commit = offwhen you want that trade, and record the bounded loss window it accepts. - Leave
full_page_writeson unless the platform documents atomic 8 KiB writes, and budget its WAL cost. - Ask the storage questions explicitly and record the answers. PostgreSQL cannot detect a layer that acknowledges early.
- Re-ask after any infrastructure change. A hypervisor migration, a SAN firmware update or a move to different instance storage changes the answers.
- Treat a controller battery as a durability dependency, and check that its health is monitored.
Cross-course references
- Linux for Production Sysadmins — Part XIII (Disks), Part XV (Fstab) and Part XLI (Disk performance) cover mount options, write barriers and device caches directly.
- Ceph & Distributed Storage — Part II (Storage performance) and Part XII (BlueStore) cover what a distributed storage layer does with a flush, which is the same question asked of a different platform.
- Proxmox — Part V (Storage fundamentals) covers hypervisor disk cache modes, which is where the guest’s flush is most often acknowledged early.
Quiz
Knowledge check · 6 questions
Q1. What is the essential difference between fsync = off and synchronous_commit = off after an operating-system crash?
Q2. Why does full_page_writes exist?
Q3. Which layers can acknowledge a write before it is on stable media, without PostgreSQL being able to detect it? Select all that apply.
Q4. COMMIT returns only after the modified data pages have been written to disk.
Q5. Name three questions to ask a storage or virtualisation team to establish whether a COMMIT is a genuine durability promise.
Q6. Assess the change and state what must be established before it proceeds.
A team reports that a nightly bulk load takes four hours and proposes setting fsync = off for the duration of the load, then setting it back and reloading afterwards. The database is the production order-management system, 900 GB, with a documented RPO of five minutes. They note that the load runs on a replica-free single instance during a maintenance window when no users are connected, and that they will take a backup beforehand.
Passing score: 75%. Answers are checked in this browser.