PostgreSQL 19: What's New and What to Test Before You Upgrade
PostgreSQL 19 is due for general availability on November 19, 2026, a week after the final PostgreSQL 14 release. Release candidate 2 shipped on November 12, and unless something turns up, it's what you'll get on Thursday.
So this is a good week to test, not to migrate. Here's what's new, which changes can break an upgrade, and how to try 19 in Docker without touching production.
What's New in PostgreSQL 19
The highlights from the PostgreSQL 19 release notes:
REPACK. A new command that reclaims disk space and reorganizes a table, andREPACK (CONCURRENTLY)does it without blocking reads and writes. If you've been reaching for an extension or a maintenance window to fight bloat, this is the one to try first.- Plan advice. The
pg_plan_adviceextension lets you steer planner decisions, andpg_stash_adviceapplies that advice automatically to matching queries. - Logical replication without a restart, as long as
wal_levelisreplica. Sequence values now replicate too. - "Read your writes" on standbys through a new
WAITcommand, so an app can write to the primary and safely read the result from a replica. - Autovacuum improvements: indexes can be vacuumed in parallel, and a new scoring system decides which tables go first.
- Performance work, including automatic scaling of I/O worker processes and faster foreign key checks. New defaults also use
lz4for TOAST compression.
The Changes That Can Break an Upgrade
The "Migration to Version 19" section is longer than usual. These are the items most likely to bite:
| Change | What to do |
|---|---|
standard_conforming_strings is now always on | Old dumps made with it off won't load. Take your dump with the 19 tools. |
inet/cidr indexes using btree_gist | pg_upgrade refuses to run. Rebuild them with the GiST opclass first. |
Full text search and pg_trgm now follow the database's locale provider | Reindex those indexes after upgrading and recompute stored tsvector values. |
fuzzystrmatch functions changed | Reindex any index built on them. |
| JIT is off by default | Heavy analytics workloads may need to turn it back on. |
max_locks_per_transaction default doubled to 128 | Lock sizing changed, so double any custom value to keep the same capacity. |
io_workers removed | Replace it with the new io_min_workers and io_max_workers settings. |
| RADIUS authentication removed | Move those users to another method before upgrading. |
Two smaller ones: MD5 password logins now log a warning every time (MD5 has been deprecated since 18), and json_array() returns an empty array instead of NULL when it finds no rows.
If You Tested the Release Candidates
RC 2 reverted a change from earlier test builds. JSON_ARRAY() and JSON_OBJECT() expressions over arrays, composite types and range types can no longer be used in expression indexes, generated columns or partition keys, because those indexes could go stale. If you created any of those while testing, the RC 2 announcement is clear: drop or redefine them before upgrading, or pg_dump and pg_upgrade will fail to recreate them.
Should You Upgrade on Thursday?
My take: test on Thursday, and plan the production move for after the first update release. PostgreSQL's next scheduled minor releases are on February 11, 2027, and new major versions usually pick up a handful of fixes in that first round.
There's no deadline pushing you. PostgreSQL 18 is supported until November 2030, and 17 until November 2029. The exception is anyone still on 14, which stopped getting fixes this week. Move to 18 now using our PostgreSQL 14 upgrade guide, and you'll have years before you need 19.
Trying 19 in Docker
The safest test is a restore of your real data into a throwaway 19 container. Until Thursday that's the 19rc2 image tag (the official image publishes release-candidate tags such as 19rc1), then postgres:19 once it's out.
docker run -d --name pg19-test \
-e POSTGRES_PASSWORD=test \
-v pg19-test:/var/lib/postgresql \
postgres:19rc2
As in 18, the official image keeps its data in a version-specific folder and expects the volume at /var/lib/postgresql, not /var/lib/postgresql/data. Dump your current database with the 19 client tools, which avoids the standard_conforming_strings problem:
docker exec -e PGPASSWORD=YOUR_DB_PASSWORD pg19-test pg_dumpall -h YOUR_DB_HOST -U postgres > dump-for-19.sql
docker exec -i pg19-test psql -U postgres < dump-for-19.sql
Replace YOUR_DB_HOST and YOUR_DB_PASSWORD with your current server's address (reachable from the test container) and password. You'll see a harmless role "postgres" already exists error during the restore. Then run your app's test suite against it, and look closely at analytics queries (JIT), text search results (the locale change) and anything that takes many locks in one transaction.
On Elestio, PostgreSQL starts at $11/month for the VM. A Clone of your service is an easy way to rehearse the real upgrade later, and AI DevOps can run it for you when you're ready.
Troubleshooting
pg_upgrade stops on an inet or cidr index. It's a btree_gist index, which 19 refuses to carry over. Recreate it with the default GiST opclass before upgrading.
An old dump won't load. It was probably made with standard_conforming_strings off. Dump again using the 19 pg_dump.
Restore fails on a JSON expression index or generated column. That's the RC 2 revert. Drop or redefine the object on the source, then dump again.
Text search results changed. That's the locale-provider change. Reindex and recompute your tsvector columns.
Analytics queries got slower. JIT is off by default now. Set jit = on and compare.
"Out of shared memory" on lock-heavy jobs. Lock sizing changed. Double your custom max_locks_per_transaction.
Thanks for reading ❤️ See you in the next one 👋