PostgreSQL 18 I/O Observability: What the New pg_stat_io Metrics Change
PostgreSQL 18 adds byte-level pg_stat_io metrics and WAL I/O visibility. Learn what changed, how to read the new counters, and what managed PostgreSQL teams should monitor after upgrading.
PostgreSQL 18 makes one of the most practical monitoring views easier to use in production. The release adds byte-level counters to pg_stat_io, extends the view to cover WAL I/O activity, and moves WAL timing into the same I/O statistics model. For teams running managed PostgreSQL, that means fewer guesses when a database is slow because storage is busy rather than CPU-bound.
The change is not a replacement for provider metrics, query plans, or application tracing. It is a better database-native layer for answering a basic operational question: what kind of I/O is PostgreSQL doing, and where is it coming from?
What changed in PostgreSQL 18
PostgreSQL 16 introduced pg_stat_io as a structured view of backend, background writer, checkpointer, autovacuum, and other I/O activity. PostgreSQL 18 makes that view more useful by reporting read, write, and extend activity in bytes through read_bytes, write_bytes, and extend_bytes. The release notes also state that op_bytes was removed because it always equaled BLCKSZ, which is commonly 8 kB but is not the same as an actual workload volume metric.
The second important change is WAL visibility. PostgreSQL 18 adds WAL I/O activity rows to pg_stat_io, including WAL receiver activity, and changes track_wal_io_timing so WAL timing is tracked in pg_stat_io rather than pg_stat_wal. At the same time, the old read and sync columns are removed from pg_stat_wal. In practice, this nudges operators toward one place for I/O timing and volume questions instead of splitting relation I/O and WAL I/O across unrelated views.
For a managed database customer, the practical benefit is sharper escalation and tuning. If application latency rises, you can separate buffer cache misses, relation writes, WAL writes, and extension of relation files before assuming the plan is bad or the instance is undersized.
Why byte counters matter
Operation counts are useful, but they are easy to misread. Ten thousand small reads and ten thousand larger reads are not the same storage event, and a graph of reads alone may hide the difference. Byte counters let you compare database-visible I/O volume with storage throughput, backup windows, import jobs, and provider-level disk metrics.
That does not make the counters a perfect billable-throughput source. PostgreSQL statistics describe what PostgreSQL asked for and observed internally. The cloud provider may report at a different layer, with different caching, block device, filesystem, or replication behavior. The value is correlation. If pg_stat_io shows relation read bytes climbing at the same time provider disk read throughput and query latency climb, you have a much stronger diagnosis than any single chart provides.
| New or changed metric | Where to look | What it helps answer | Operational note |
|---|---|---|---|
| read_bytes | pg_stat_io | How much data PostgreSQL read for a context and object type | Compare with cache hit rates and storage read throughput before adding indexes blindly. |
| write_bytes | pg_stat_io | How much data PostgreSQL wrote outside normal WAL-only thinking | Useful during bulk loads, autovacuum work, and checkpoint-heavy periods. |
| extend_bytes | pg_stat_io | How much relation file growth PostgreSQL performed | Helpful when imports, tenant growth, or append-heavy tables create allocation pressure. |
| WAL rows in pg_stat_io | pg_stat_io where object is wal | Whether WAL reads and writes are part of the observed I/O pressure | Important for write-heavy applications and replication catch-up analysis. |
| track_wal_io_timing | server setting | Whether WAL I/O timing is collected in pg_stat_io | Timing counters are zero unless the relevant timing setting is enabled. |
The table is deliberately small because the first upgrade task is not to build a giant dashboard. It is to replace vague alerts such as disk is high with a few database-native questions that point to the next action.
A practical query to start with
After upgrading to PostgreSQL 18, start with a compact rollup. The exact columns in pg_stat_io include backend_type, object, context, reads, read_bytes, writes, write_bytes, extends, extend_bytes, evictions, reuses, fsyncs, and timing fields. A useful first query is to group by the dimensions that explain who did the work and what kind of object was touched.
SELECT
backend_type,
object,
context,
pg_size_pretty(sum(read_bytes)::bigint) AS read_volume,
pg_size_pretty(sum(write_bytes)::bigint) AS write_volume,
pg_size_pretty(sum(extend_bytes)::bigint) AS extend_volume,
sum(reads) AS reads,
sum(writes) AS writes,
sum(extends) AS extends
FROM pg_stat_io
GROUP BY backend_type, object, context
ORDER BY sum(read_bytes + write_bytes + extend_bytes) DESC;
Read this as a triage view, not as a verdict. High client backend reads may point toward queries that are missing useful indexes, reading too much history, or working with a cold cache after failover. High autovacuum writes may be normal maintenance catching up after a write burst. High checkpoint or background writer activity may suggest checkpoint configuration, write rate, or storage behavior deserves attention. WAL I/O deserves separate interpretation because a healthy write-heavy system must generate WAL; the question is whether WAL latency or volume matches the application symptom.
If you use a managed PostgreSQL provider, keep a copy of this query in the runbook next to provider graphs for IOPS, throughput, disk latency, CPU, and memory. The database view tells you which PostgreSQL component observed the activity. The provider graph tells you whether the underlying platform was saturated or slow at the same time.
How this changes upgrade planning
PostgreSQL 18 I/O observability is especially useful during upgrade rehearsals. Before the production cutover, capture pg_stat_io snapshots on the old environment where available, then capture PostgreSQL 18 snapshots during load tests and after the first production traffic window. The byte counters make it easier to compare the shape of the workload even when query timing changes for other reasons.
A good rehearsal looks at three moments. First, record baseline I/O during normal traffic so you know the ordinary read/write mix. Second, observe a maintenance-heavy period such as vacuum, index creation, restore validation, or a bulk import. Third, observe failover or cache-warmup behavior if your platform allows a safe test. These moments reveal different bottlenecks, and pg_stat_io is useful precisely because it separates contexts rather than flattening everything into one storage number.
Do not overfit the first day of metrics. PostgreSQL cumulative statistics reset, workload composition changes, and managed providers may move instances during maintenance. The right pattern is to snapshot, annotate, and compare against application symptoms. A small table in your incident notes with timestamp, deploy state, provider disk latency, top pg_stat_io rows, and top slow queries is often more useful than another permanent dashboard panel.
What to watch after the upgrade
The most common mistake is treating I/O counters as a standalone performance diagnosis. If read_bytes is high, the answer might be better indexing, but it might also be a deliberate analytics query, a cold cache, a changed plan, or a table scan that is perfectly acceptable off the request path. If write_bytes is high, the answer might be checkpoint tuning, but it might also be an import job or autovacuum doing necessary cleanup.
Pair pg_stat_io with query-level tools. pg_stat_statements remains the natural place to find statements with high execution time, high block reads, or unexpectedly large call counts. EXPLAIN with buffers is still the right tool when one query looks suspicious. PostgreSQL 18 also adds parallel worker counters to pg_stat_database and pg_stat_statements, which can help distinguish a storage bottleneck from a plan that expected parallelism but did not get enough workers.
For teams using ArmorDB or another managed PostgreSQL service, the useful habit is to keep responsibilities clear. Let the platform handle server maintenance, backups, and routine operations, but keep enough SQL-level observability to understand your workload. When you contact support or decide whether to scale, a pg_stat_io snapshot plus slow-query evidence is much more actionable than a screenshot of a single high-latency graph.
If you are still designing the surrounding architecture, this is also a good moment to review connection behavior. Excess connections can amplify latency symptoms and make every investigation noisier. Our guide to PgBouncer pooling modes explains how session, transaction, and statement pooling affect application behavior.
Takeaway
PostgreSQL 18 turns pg_stat_io into a better first stop for production I/O questions. Byte counters make the volume visible, WAL rows bring write-ahead log activity into the same mental model, and timing settings clarify when the database is measuring wait time rather than only counting operations.
The upgrade does not remove the need for provider metrics or query analysis. It gives you a cleaner bridge between them. When the next latency incident arrives, start by asking which PostgreSQL component is reading, writing, extending, or waiting on WAL, then move from that evidence to query plans, workload changes, or platform scaling.
Sources and further reading
Topic
Tech-News & Trends
Updated
Jul 9, 2026
Read time
7 min read
ArmorDB Engineering writes about PostgreSQL operations, security, and infrastructure decisions for teams building production apps on ArmorDB.
Read next
Data-Specs / Vergleiche · 8 min read
Serverless vs Provisioned PostgreSQL: How to Choose for Production
Compare serverless and provisioned PostgreSQL for latency, cost, pooling, operations, and production readiness before you choose a managed database architecture.
Read articleShort-Form & Quick Fixes · 5 min read
Fix PostgreSQL Canceling Statement Due to Conflict With Recovery
Learn why standby queries can be canceled by PostgreSQL recovery conflicts, how to diagnose the cause, and what to change safely in managed PostgreSQL.
Read article