PostgreSQL
What each PostgreSQL version changed under your monitoring
One page per change, run against the exact minor it names, with the old query and the new one side by side and a word on how the old one fails.
A major upgrade rarely breaks an application, and it fairly often breaks the thing watching the application. The statistics views are not covered by the compatibility promise that the SQL surface enjoys, so a column that has counted checkpoints since 9.2 can leave in 17 with a release-note sentence and nothing else. Some of those changes error, and somebody fixes the dashboard the same afternoon. The worse ones return an empty set, and an empty set on a checkpoint panel looks like a quiet cluster.
These pages exist to make that list checkable rather than remembered. Each takes one change in one version, runs the same question against that version and the one before it in a container pinned to an exact minor, and pastes what came back. Where the old query is gone, the page shows the error you will see. Where it merely goes quiet, the page says so, and names the exporter metric or the integration that broke on it, because somebody else has usually hit it first. The verification strip on every page names the minors and the date; that is a claim, and the receipts behind it are in the repository that builds this site.
The table below is the support calendar from postgresql.org, written as dates rather than as an assumption about what "supported" means. A version listed here has a hub only once it has at least one page, so a hub is never empty and a version showing none yet is a gap rather than a promise. PostgreSQL 14 leaves support on 12 November 2026, which is the date most of the fleets reading this are working to. For the failure modes rather than the version history, read the reference guides; for the words the release notes assume you know, the glossary.
| Version | Released | Supported until | Pages |
|---|---|---|---|
| PostgreSQL 18 | 25 September 2025 | 14 November 2030 | 14 so far |
| PostgreSQL 17 | 26 September 2024 | 8 November 2029 | 14 so far |
| PostgreSQL 16 | 14 September 2023 | 9 November 2028 | 7 so far |
| PostgreSQL 15 | 13 October 2022 | 11 November 2027 | 8 so far |
| PostgreSQL 14 | 30 September 2021 | 12 November 2026 | 11 so far |
PostgreSQL 18
-
PostgreSQL 18 breaks: none
The I/O workers PostgreSQL 18 starts
PostgreSQL 18 issues reads through an asynchronous layer with worker processes of its own. New rows appear in the process list, counting nothing at all.
-
PostgreSQL 18 breaks: none
pg_aios answers only while you watch
PostgreSQL 18 exposes in-flight asynchronous I/O in a view. Query it from an idle session and it is empty, which is correct and misleading in equal measure.
-
PostgreSQL 18 breaks: silent
A read stopped being a block in 18
PostgreSQL 18 merges adjacent reads into one larger request, so the read count in pg_stat_io falls by an order of magnitude while the volume stays the same.
-
PostgreSQL 18 breaks: errors
op_bytes is gone and bytes are counted
The multiplier every I/O dashboard used to turn operations into volume was deleted in PostgreSQL 18, and three byte columns arrived to replace the arithmetic.
-
PostgreSQL 18 breaks: none
I/O attributed to one backend at last
PostgreSQL 18 can report the reads, writes and log bytes of a single session. The numbers are excellent and they disappear the moment the session does.
-
PostgreSQL 18 breaks: silent
One reset call now clears half the log
On PostgreSQL 18 the call that used to clear every write-ahead log counter clears only some of them. It succeeds, and the rest keep accumulating since boot.
-
PostgreSQL 18 breaks: none
How much of a vacuum is spent asleep
PostgreSQL 18 reports the time vacuum and analyze spend throttled by the cost delay, live in the progress views and in the log line when they finish.
-
PostgreSQL 18 breaks: none
What each table costs to keep vacuumed
PostgreSQL 18 records the cumulative time spent vacuuming and analyzing each table, which turns maintenance from a count of runs into a bill you can rank.
-
PostgreSQL 18 breaks: none
Checkpoints requested, and the ones run
PostgreSQL 18 counts checkpoints that actually completed. The two older counters include the ones the server decided to skip, which nobody was told.
-
PostgreSQL 18 breaks: silent
log_connections stopped being a boolean
In PostgreSQL 18 the connection logging switch takes a list of stages instead of on or off, and one of them finally times how long connecting actually took.
-
PostgreSQL 18 breaks: silent
EXPLAIN ANALYZE says more than it did
PostgreSQL 18 adds buffer counts to every analyzed plan without being asked, and changes the row counts it prints from integers into decimals.
-
PostgreSQL 18 breaks: none
Parallel workers asked for, and granted
PostgreSQL 18 counts the parallel workers each database wanted and the ones it got. The gap between them is the cheapest capacity signal in the release.
-
PostgreSQL 18 breaks: none
Replication conflicts became countable
Before PostgreSQL 18 a logical replication conflict that did not stop the subscriber left no trace in any view. Version 18 counts each kind separately.
-
PostgreSQL 18 breaks: none
Slots that expire when nobody reads
PostgreSQL 18 can invalidate a replication slot that has gone unread for long enough, which trades an unbounded disk problem for a broken consumer.
PostgreSQL 17
-
PostgreSQL 17 breaks: errors
pg_stat_checkpointer replaced pg_stat_bgwriter
PostgreSQL 17 moved the checkpoint counters into a view of their own and deleted two columns outright. What errors, what goes quiet, and what to read instead.
-
PostgreSQL 17 breaks: errors
The statements view renamed its I/O timers
PostgreSQL 17 gave two pg_stat_statements timing columns a scope in their names. Every query that ranks statements by I/O time stops on the first one.
-
PostgreSQL 17 breaks: none
Knowing how old a statements row really is
A reset used to leave you guessing which counters had been running since when. PostgreSQL 17 stamps every pg_stat_statements row with its own start.
-
PostgreSQL 17 breaks: errors
Vacuum progress stopped counting in tuples
Two pg_stat_progress_vacuum columns were renamed in PostgreSQL 17 and one of them changed units. A threshold set in tuples is now wrong twice over.
-
PostgreSQL 17 breaks: none
Vacuum finally says how many indexes are left
Until PostgreSQL 17 the index phase of a vacuum was a black box with no denominator. Two new columns turn it into something you can actually watch.
-
PostgreSQL 17 breaks: none
Wait events that arrive with their own glossary
PostgreSQL 17 added a catalog of every wait event the server knows, each with a description. Join it to pg_stat_activity and the dashboard explains itself.
-
PostgreSQL 17 breaks: none
Slots that say why they died and when
A dead replication slot on PostgreSQL 16 tells you it is lost and nothing more. Version 17 adds the reason it was invalidated and the moment it went idle.
-
PostgreSQL 17 breaks: none
Vacuum is no longer capped at one gigabyte
PostgreSQL 17 replaced the flat array of dead row pointers with a store that grows. A big table needs fewer index passes, and the budget is finally honest.
-
PostgreSQL 17 breaks: silent
Every SLRU cache but one changed its name
The rows in pg_stat_slru are keyed by name, and PostgreSQL 17 rewrote the keys. Filters return nothing and a reset call quietly clears the wrong cache.
-
PostgreSQL 17 breaks: none
EXPLAIN can price the wire and the planner
SERIALIZE and MEMORY arrived in PostgreSQL 17. One of them explains the class of slow query where the plan is fast and the client still waits.
-
PostgreSQL 17 breaks: none
A reset call that now wipes everything
On PostgreSQL 16 a bare pg_stat_reset_shared refused to run and a null argument did nothing. On 17 both clear every shared statistics view at once.
-
PostgreSQL 17 breaks: none
pg_upgrade carries logical slots only from 17
The slot migration added in PostgreSQL 17 is about upgrades out of 17, not into it. A 14, 15 or 16 source still loses every logical slot, silently.
-
PostgreSQL 17 breaks: silent
log_connections now logs trust connections
PostgreSQL 17 writes a connection authenticated line for trust logins as well. What that adds to the log, and which log rules quietly change answer.
-
PostgreSQL 17 breaks: errors
pg_stat_subscription gained a worker type
PostgreSQL 17 names each logical replication worker instead of leaving you to infer it. Where the new column landed, and what a SELECT star breaks on.
PostgreSQL 16
-
PostgreSQL 16 breaks: none
pg_stat_io and the end of I/O by subtraction
Before PostgreSQL 16 you could see how many blocks a cluster read but not who read them. The view that answered it, shown beside the arithmetic it retired.
-
PostgreSQL 16 breaks: none
The timestamp that dates an unused index
PostgreSQL 16 records when a table was last scanned sequentially and when it was last reached through an index. What that timestamp actually measures.
-
PostgreSQL 16 breaks: none
The update counter fillfactor was waiting for
PostgreSQL 16 counts updates whose new row version had to go on a different page. It is not the same number as non-HOT updates, and the gap is the point.
-
PostgreSQL 16 breaks: none
Reading a backend's subtransaction cache
PostgreSQL 16 can report how many subtransactions a session is holding and whether its cache has overflowed. Watching it move takes one more call than expected.
-
PostgreSQL 16 breaks: none
The slot column that is null when it matters
Logical decoding on a standby arrived in PostgreSQL 16 and brought a conflict column with it. Physical slots leave it null, which quietly breaks slot alerts.
-
PostgreSQL 16 breaks: none
The subscription reset that did nothing
On PostgreSQL 15 resetting a subscription's error counters could succeed and change nothing. PostgreSQL 16 creates the statistics entry up front and fixes it.
-
PostgreSQL 16 breaks: silent
The temp reads that were really extends
PostgreSQL 16 corrected how temporary relation I/O is accounted in pg_stat_database. The cache hit ratio moves at the upgrade with no column having changed.
PostgreSQL 15
-
PostgreSQL 15 breaks: errors
The statistics collector process is gone
PostgreSQL 15 moved cumulative statistics into shared memory. The setting that pointed them at a ramdisk is gone, and a copied config file will not start.
-
PostgreSQL 15 breaks: none
The default that freezes your counters
PostgreSQL 15 lets a session choose whether statistics are read live or frozen for the transaction. The default is frozen, which is why a watched query stalls.
-
PostgreSQL 15 breaks: none
JIT counters, and the extension version trap
PostgreSQL 15 added just-in-time compilation counters to pg_stat_statements. Upgrading the server is not what makes them appear, which catches most fleets once.
-
PostgreSQL 15 breaks: silent
The spill that was never on the clock
PostgreSQL 15 times temporary block I/O and qualifies the EXPLAIN timing labels. A plan's dominant cost had been uncounted, and log parsers stop matching.
-
PostgreSQL 15 breaks: none
The view that shows a subscriber looping
PostgreSQL 15 added error counters for logical replication subscriptions. Before it, an apply worker failing every few seconds looked like a healthy one.
-
PostgreSQL 15 breaks: none
When wal_compression stopped being a boolean
PostgreSQL 15 turned a true-or-false setting into a choice of algorithms. Measuring the difference honestly takes more care than the obvious experiment.
-
PostgreSQL 15 breaks: none
The archiver says what it is waiting on
PostgreSQL 15 gave archiving a library interface and gave the archiver process wait events. The statistics view it reports through did not change at all.
-
PostgreSQL 15 breaks: none
The idle server that started writing logs
PostgreSQL 15 turned on checkpoint logging and slow autovacuum logging by default. A cluster whose config was copied forward keeps the old silence instead.
PostgreSQL 14
-
PostgreSQL 14 breaks: errors
The PostgreSQL 14 end-of-life audit
PostgreSQL 14 has its final release on 12 November 2026. What to check on a fleet still running it, and which monitoring queries break on the way out.
-
PostgreSQL 14 breaks: silent
compute_query_id moved into the server
PostgreSQL 14 computes the query identifier in core and exposes it in pg_stat_activity, verbose plans and the log prefix. The join key, and its half-life.
-
PostgreSQL 14 breaks: errors
pg_stat_wal, and the columns 18 takes back
PostgreSQL 14 made write-ahead log generation readable from SQL. Four of its columns are gone on 18, and the timing they carried moved somewhere else.
-
PostgreSQL 14 breaks: silent
pg_stat_replication_slots and decoding spill
PostgreSQL 14 made logical decoding spill visible per slot. Why the counters say a subscriber is being fed from disk, and the join that returns nothing.
-
PostgreSQL 14 breaks: none
pg_stat_progress_copy, and the frozen row
PostgreSQL 14 made bulk loads observable while they run. Where the byte total comes from, and why the row stops moving when you watch it from inside.
-
PostgreSQL 14 breaks: errors
Reading a backend's memory from SQL
PostgreSQL 14 exposed memory contexts as a view and added a function to dump another session's. Useful on 14, and shaped differently from 18 onward.
-
PostgreSQL 14 breaks: none
vacuum_failsafe_age and emergency mode
PostgreSQL 14 gave vacuum an emergency gear that abandons cost limits near wraparound. What it looks like when it fires, and the floor under the setting.
-
PostgreSQL 14 breaks: silent
toplevel, and the sum that counts twice
PostgreSQL 14 marked nested statements in pg_stat_statements and added a view of the extension's own health. Two columns that change what a total means.
-
PostgreSQL 14 breaks: none
Lock waits got a start time in pg_locks
PostgreSQL 14 recorded when a lock wait began, so a blocking tree can carry durations. What the column holds, and the two cases where it is null.
-
PostgreSQL 14 breaks: none
Session statistics in pg_stat_database
PostgreSQL 14 added session counts and time in each state to the per-database view. Where idle-in-transaction time comes from, and how sessions end.
-
PostgreSQL 14 breaks: none
pg_amcheck runs verification in one command
PostgreSQL 14 added a command that checks every relation in a database for corruption. What it checks, what it quietly skips, and what its exit codes mean.
A fleet on more than one of these majors reads the same signal from different places on each. That is what engine discovery is for, and why fleet observability reads a signal from wherever the version keeps it.
Put every Postgres you run on autopilot.
We onboard teams in small batches. Tell us about your fleet and we will reach out when a seat opens. One email, no drip campaign.