PostgreSQL

PostgreSQL WAL Configuration: fsync, synchronous_commit, and Checkpoint Tuning

Tune PostgreSQL 18 WAL and checkpoints without weakening crash safety: understand commit modes, measure checkpointer deltas, and validate archive recovery.

JusDB Team
Published October 16, 2025
Updated August 1, 2026
6 min read

PostgreSQL writes changes to write-ahead log (WAL) before changed data pages reach their table and index files. WAL makes crash recovery and physical replication possible. Tuning it safely means identifying where time is spent, preserving crash-safety invariants, and changing checkpoint or commit behavior only for a stated durability objective.

In short
  • Keep fsync=on and full_page_writes=on for production crash safety.
  • synchronous_commit changes when success is acknowledged; its standby modes matter only when synchronous standbys are configured.
  • On PostgreSQL 17 and later, checkpoint counters are in pg_stat_checkpointer, not pg_stat_bgwriter.
  • max_wal_size is a soft checkpoint target, not a hard disk cap.
  • A WAL archive is trustworthy only after a restore and recovery test.

The settings that protect recovery

fsync=on asks PostgreSQL to ensure updated data reaches durable storage in the required order. Turning it off can leave the cluster unrecoverable or silently inconsistent after an operating-system or hardware failure. Faster storage is a reason to measure lower sync latency, not a reason to disable the protection.

full_page_writes=on logs a full page image the first time a page is modified after a checkpoint, protecting recovery from a partially written data page. Disabling it can produce unrecoverable corruption after a crash unless the storage stack provides an equivalent atomic-write guarantee that has been proven end to end. Keep it on.

wal_sync_method selects the operating-system primitive used for WAL synchronization. Available choices vary by platform. Benchmark supported values only on the actual storage and kernel, then retest crash recovery; do not transplant a value from another environment.

Understand synchronous_commit precisely

ValueWhat COMMIT waits for
remote_applyLocal WAL flush plus replay on the required synchronous standby or standbys.
onLocal WAL flush plus durable WAL flush on required synchronous standby or standbys.
remote_writeLocal flush plus confirmation that required standbys wrote WAL to their operating systems, not necessarily durable storage.
localLocal WAL flush, without waiting for a synchronous standby.
offNo wait for local WAL flush before reporting success.

The remote modes require a nonempty synchronous_standby_names configuration and an eligible synchronous standby. Without synchronous standbys, remote_apply, on, and remote_write provide the same local synchronization behavior. With off, a database crash can lose recently acknowledged transactions, but PostgreSQL still writes WAL records in order and does not intentionally risk database inconsistency.

Use a transaction-local relaxation only for work whose loss can be regenerated:

sql
BEGIN;
SET LOCAL synchronous_commit = 'off';
INSERT INTO rebuildable_event_stage(payload) VALUES ('...');
COMMIT;

SET LOCAL must run inside a transaction to affect that transaction. Do not use the setting for payments, identity changes, schema migrations, or other writes whose acknowledged loss violates the service contract.

WAL buffers and compression

The default wal_buffers=-1 lets PostgreSQL select one thirty-second of shared_buffers, with a minimum of 64 kB and a maximum of one WAL segment—typically 16 MB. The automatic value is reasonable for most systems. Increase it only when evidence shows WAL buffer pressure, such as WAL writes triggered because buffers fill, and compare the complete workload.

wal_compression can reduce full-page-image WAL at the cost of CPU. PostgreSQL supports pglz; lz4 and zstd are available only when PostgreSQL was built with their support. Compression results depend on page contents, CPU, storage, and replication bandwidth, so benchmark rather than promising a fixed reduction or CPU cost.

Checkpoint mechanics and defaults

A checkpoint writes dirty buffers so recovery can begin from a known WAL location. PostgreSQL 18 defaults include checkpoint_timeout=5min, checkpoint_completion_target=0.9, max_wal_size=1GB, and min_wal_size=80MB. These are reference defaults, not recommended values for every workload.

A checkpoint starts after the timeout or when projected WAL use reaches the checkpoint threshold derived from max_wal_size. The maximum is soft: WAL can exceed it under heavy load, a failing archive command, replication slots, or other retention needs. Size disk for worst credible WAL retention rather than relying on the setting as a cap.

A higher checkpoint_completion_target spreads writes through more of the interval; 0.9 is already the current default. Increasing max_wal_size can reduce requested checkpoints but increases possible recovery work and disk requirements. Tune both from measured checkpoint write/sync time, WAL generation, recovery objectives, and storage behavior.

Use the current statistics views

PostgreSQL 17 moved checkpoint counters out of pg_stat_bgwriter. On PostgreSQL 17 and 18:

sql
SELECT num_timed,
       num_requested,
       write_time,
       sync_time,
       buffers_written,
       stats_reset
FROM pg_stat_checkpointer;

SELECT buffers_clean,
       maxwritten_clean,
       buffers_alloc,
       stats_reset
FROM pg_stat_bgwriter;

On PostgreSQL 16 and earlier, use that version's documented pg_stat_bgwriter columns. Sample counters at two times and compare deltas. Requested checkpoints are not automatically a fault—an explicit CHECKPOINT also requests one—but sustained growth alongside latency, WAL pressure, or storage saturation deserves investigation.

Avoid resetting all statistics merely to simplify arithmetic. If an authorized diagnostic session truly needs only checkpointer counters reset, PostgreSQL provides SELECT pg_stat_reset_shared('checkpointer');. Recording a sample timestamp and calculating deltas is usually safer.

Read log messages correctly

checkpoint_warning logs when checkpoints caused by WAL consumption occur closer together than the configured warning interval. It does not mean that a checkpoint itself took longer than that interval. Enable log_checkpoints when checkpoint timing detail is needed, then correlate write and sync phases with database latency and storage telemetry.

Host tools such as iostat are useful, but no universal percentage-utilization threshold diagnoses every SSD or NVMe device. Compare latency, queue depth, throughput, CPU wait, and device-specific behavior with an established healthy baseline.

WAL archiving must prove durability

properties
archive_mode = on
archive_command = '/usr/local/sbin/archive-wal "%p" "%f"'

This is a contract for an administrator-owned script, not a complete archive implementation. The script must copy the requested WAL segment to durable storage, handle an already archived segment idempotently, and return zero only after success. Restrict its permissions, monitor repeated failures and archive backlog, and ensure retained slots or failed archiving cannot fill pg_wal.

archive_timeout forces a segment switch only when there has been database activity. It does not guarantee a new archive object at every interval, and a very short value can waste archive storage because early-switched segments are still full-size files. Set it from the acceptable archive recovery-point window and storage cost.

A safe tuning workflow

  1. State the durability, recovery-point, recovery-time, and replica-read requirements.
  2. Measure WAL bytes, commit latency, checkpointer deltas, archive lag, slot retention, storage latency, and user-facing latency through a representative peak.
  3. Identify whether commit flush, checkpoint writes, archive throughput, or unrelated query I/O is the constraint.
  4. Change one setting at a time and test steady state plus crash recovery and replica catch-up.
  5. Load-test the resulting WAL volume and verify disk headroom under archive or replica failure.
  6. Restore a base backup and replay archived WAL to the intended point before accepting the design.

Official primary sources

Working with JusDB on PostgreSQL WAL

JusDB helps teams measure WAL and checkpoint bottlenecks, preserve durability guarantees, capacity-plan retention, and validate archive restoration under production-shaped load.

Explore JusDB PostgreSQL services →  |  Talk to a PostgreSQL engineer

Share this article

Database engineering notes

Articles like this one, in your inbox. No spam, unsubscribe anytime.

JusDB Team

Official JusDB content team

Keep reading

PostgreSQL 19 Beta: Every New Feature That Matters to DBAs

PostgreSQL 19 Beta 1 (June 4, 2026) brings parallel autovacuum, the native REPACK command for online table rebuilds, 2x faster inserts under foreign-key load, online logical replication without a restart, WAIT FOR LSN for read-your-writes consistency, and default changes (JIT off, lz4 TOAST, RADIUS removed). A DBA-focused walkthrough of what changed and what to test before GA.

PostgreSQL14 minJun 15, 2026
Read

PostgreSQL Performance Tuning Playbook: A Top-Down Method for Faster Queries

A repeatable, top-down method for tuning PostgreSQL: measure with pg_stat_statements, read plans with EXPLAIN (ANALYZE, BUFFERS), fix queries and indexes before parameters, then tune memory, I/O, WAL, connection pooling, and autovacuum — with a ready-to-adapt postgresql.conf baseline.

PostgreSQL22 minMay 31, 2026
Read

PostgreSQL Architecture Deep Dive: Process Model, MVCC, WAL & Replication Explained

Walk through PostgreSQL's multi-process architecture, shared/local memory layout, page-organized storage, MVCC tuple versioning, the WAL write path, the query execution pipeline, and physical + logical replication — all with ASCII flow diagrams that show how data and control actually move through the system.

PostgreSQL18 minMay 31, 2026
Read