The PostgreSQL "fsync" Paradox: Why the Most Dangerous Setting Requires Caution

In the vast ecosystem of PostgreSQL configuration parameters, few carry the weight and peril of fsync. Often misunderstood by novice database administrators and sometimes recklessly employed by performance-seeking engineers, this single boolean parameter represents the fine line between a robust, enterprise-grade database and a pile of corrupted, unrecoverable data.
While most configuration settings in postgresql.conf—such as memory allocation or query planning parameters—typically result in degraded performance or inefficient resource usage when misconfigured, fsync stands alone. It is, unequivocally, the most dangerous setting in the PostgreSQL arsenal. When set incorrectly, you do not merely suffer a "bad plan" or "wasted memory"; you risk the total loss of your cluster.
The Mechanics of Durability: Why fsync Matters
To understand the danger of disabling fsync, one must first grasp the core tenet of PostgreSQL’s durability: the Write-Ahead Logging (WAL) protocol. PostgreSQL operates on a fundamental rule—data modifications must be recorded durably in the WAL before the modified data page is permitted to reach the persistent storage. This ensures that in the event of a system crash, the database has an immutable ledger to reconstruct its state.
However, modern operating systems introduce a layer of abstraction that complicates this. When a process issues a standard write() system call, it does not actually force data onto the physical platters or NAND flash. Instead, the kernel copies those bytes into its own page cache and returns a "success" signal to the application. The kernel then decides, on its own schedule and in an order it deems most efficient, when to flush that cache to the physical disk.
The fsync() system call is the only mechanism PostgreSQL possesses to impose order upon this chaos. It acts as an instruction to the kernel: "Do not proceed until these specific bytes are physically and durably committed to the storage medium."
By setting fsync = off, you are effectively stripping PostgreSQL of its ability to demand that durability. The writes still occur, and the data eventually reaches the disk, but the critical constraints of order and timing are obliterated. The "write-ahead" guarantee transforms from a hard engineering fact into a mere aspiration.
Chronology of Failure: The Anatomy of a Crash
The consequences of disabling fsync become most apparent during a catastrophic event, such as a power failure or a kernel panic.
Consider the checkpoint process. In a healthy database, a checkpoint serves as a verified milestone: it is an assertion that everything prior to that point is durably written to the disk, allowing PostgreSQL to safely recycle older WAL segments. With fsync disabled, this assertion is a falsehood. PostgreSQL may prematurely discard WAL files that contain critical transaction data because it mistakenly believes the corresponding data pages are safely on disk.
When power is lost in this state, the aftermath is devastating:
- Incoherent State: The data files may contain partial updates that have no corresponding record in the WAL.
- Torn Pages: Individual data pages may be written partially to disk, leaving them in a corrupted, non-atomic state.
- Recovery Failure: Upon reboot, the recovery process attempts to replay the WAL against the data files. Because the WAL does not match the state of the data files—and because the data files themselves are internally inconsistent—the recovery process will fail.
You are no longer looking at a database that has lost a few seconds of work; you are looking at a directory of files that no longer constitutes a valid database. No amount of manual intervention or WAL replay can resurrect it.
Debunking the Myth: fsync vs. synchronous_commit
A frequent point of confusion among administrators is the relationship between fsync and synchronous_commit. Both are often toggled to boost write throughput, leading many to believe they are interchangeable. This is a dangerous misconception.
synchronous_commit = off is a legitimate, safe trade-off. It allows the database to report a commit as successful to the client before the WAL record is fully flushed to disk. If the system crashes, you lose the most recent, uncommitted transactions (typically within a window defined by wal_writer_delay). However, the database remains consistent. Every transaction that the database confirms as "committed" is guaranteed to be there, and any transaction lost is dropped entirely.

fsync = off, conversely, offers no such safety net. It does not trade recent work for speed; it trades the integrity of the entire dataset for speed. One is a defined, bounded risk; the other is a gamble with the entirety of your data.
The One Honest Use Case: When to Disable It
Despite the extreme risks, there is one legitimate scenario for disabling fsync: Regeneratable Data.
If a cluster is destroyed, would you simply shrug and rebuild it from a script? If the answer is "yes," then fsync = off may be an acceptable optimization. This applies to:
- Large Initial Data Loads: Restoring a dump into a fresh cluster where the source file remains available.
- CI/CD Environments: Ephemeral databases created, tested, and destroyed in a matter of minutes.
- Local Development: Instances where an
initdbcommand can restore the environment in seconds.
In these cases, the worst-case scenario is a repeat of the job. For all other production systems, fsync must remain enabled. Even if your hardware features a battery-backed write cache (BBU) or flash-backed non-volatile cache, do not disable fsync. Instead, rely on the hardware to make the fsync() call cheap, thereby maintaining the ordering guarantee while achieving the performance you desire.
The Hidden Trap: Turning fsync Back On
Because fsync has a context of sighup, it can be toggled without a server restart. Many administrators treat this as an "instant fix," flipping it back to on after a bulk load.
This is a critical error.
When you reload the configuration, the server resumes issuing fsync calls for new transactions. However, all the data written while fsync was off remains in the kernel’s page cache, un-synced. For a dangerous period after the reload, your cluster believes it is durable when it is, in fact, still vulnerable.
To safely transition back to a durable state, one must force the kernel to flush its buffers. This requires:
- Shutting down the cluster entirely.
- Running the
synccommand at the OS level. - Using
initdb --sync-only(where applicable) or unmounting the filesystem.
The standard procedure for a bulk load should be: Turn fsync off, perform the load, force a full system sync, and then turn fsync back on. Skipping that middle step leaves your database in a state of "false security."
Implications and Industry Best Practices
The PostgreSQL community’s stance on data integrity is uncompromising. When it was discovered that certain storage controllers could fail to honor fsync requests, the community responded by introducing data_sync_retry. This setting forces the database to crash rather than continue under the false assumption that a sync was successful.
If you find that fsync is a significant performance bottleneck, do not reach for the "off" switch immediately. Instead:
- Measure First: Use
pg_test_fsyncto identify if your storage is performing as expected. Often, poor performance is the result of an incorrectly chosenwal_sync_methodor subpar storage hardware. - Prioritize
synchronous_commit: If you require faster commits, adjustsynchronous_commitor configure it on a per-transaction basis to protect critical data while accelerating non-critical writes. - Avoid
full_page_writestampering: While some suggest disablingfull_page_writeswhenfsyncis off, this only compounds the risk of "torn pages."
In conclusion, the fsync parameter is not a performance lever—it is a foundational pillar of database reliability. In any environment where data durability matters, the only professional choice is to leave it on. The cost of a few milliseconds of latency is a small price to pay for the assurance that your data will survive the next power cycle.
