Postgres 18's async I/O, four months in
The biggest storage-layer change in years is in production now. Here's what it actually did to real workloads.
Postgres 18 shipped with an asynchronous I/O subsystem — the largest change to how Postgres talks to storage in a very long time. Enough people have run it in production now to say something more useful than "it is fast."
what it does#
Historically Postgres read from disk synchronously: a backend process issues a read, blocks, gets the page, continues. On a spinning disk that was fine — the disk was the bottleneck and you could not do better. On modern NVMe, where the device can service many requests in parallel and a single-threaded synchronous reader leaves most of the device idle, it wasted a great deal of available throughput.
Postgres 18 adds an I/O method abstraction:
io_method = sync # the old behavior
io_method = worker # dedicated I/O worker processes (default)
io_method = io_uring # Linux io_uring, if built with supportSequential scans, bitmap heap scans, and vacuum use it. Other paths do not yet; this is an incremental rollout across releases.
what people are seeing#
The consistent pattern from the reports I trust:
Large sequential scans on fast NVMe: substantial improvement. Analytical queries scanning a large table are the clearest win. This is exactly what you would predict — the workload was read-parallel and the code was not.
Vacuum: meaningfully faster. This matters more than it sounds. Vacuum being slow is the root of a large class of Postgres operational problems, and anything that makes it finish sooner reduces bloat pressure.
OLTP with a good cache hit rate: little change. If your working set is in shared buffers, you were not doing I/O, so making I/O faster does nothing. Most transactional workloads are in this category and should not expect much.
Cloud block storage: mixed. Network-attached storage has enough latency and enough of its own queueing that the gains are smaller and less predictable than on local NVMe. Test rather than assume.
the tuning notes#
Two settings matter and the defaults are conservative:
io_combine_limit = 128kB # how much adjacent I/O to merge into one request
effective_io_concurrency = 16 # how many concurrent reads to issueeffective_io_concurrency has historically been set based on spindle count, which is meaningless on NVMe. For a modern SSD, values well above the old defaults are appropriate. Measure — the curve flattens and then degrades, and where it turns depends on your device.
If you are on Linux with a recent kernel and can build with io_uring support, it generally outperforms the worker method by a modest margin. The worker method is the safe default and it is genuinely good.
the other things in 18 worth knowing#
Skip scan for B-tree indexes. A multi-column index on (a, b) can now be used for a query filtering only on b, when a has low cardinality. This eliminates a category of redundant index that people have been maintaining for years.
uuidv7() built in. Time-ordered UUIDs, natively. If you use UUIDs as primary keys, v7 dramatically improves index locality compared to v4, which scatters inserts across the whole B-tree. This is a real performance issue that a lot of applications have and do not know about.
OAuth authentication support, which matters for anyone trying to eliminate static database passwords.
Faster major-version upgrades, with statistics preserved across pg_upgrade. Previously you finished an upgrade with no statistics and a database that planned terribly until you ran ANALYZE across everything. That was a genuine outage risk and it is fixed.
should you upgrade#
Yes, on the normal schedule — after a point release or two, with a tested rollback, having read the release notes for the incompatibilities.
The async I/O is the headline and for most transactional workloads the statistics preservation on upgrade and uuidv7() will matter more day to day.
As always with Postgres: the release is well-tested, the migration is usually boring, and the people who get burned are the ones who skipped four major versions and tried to do it in one jump.
— Dom, January 15, 2026