PostgreSQL 19 Changes Five Defaults, Not Zero
By Nihar Ranjan Das · Wed Oct 14 2026 · 10 min read · 0 views
View as a Web StorySoftware#postgresql#postgresql 19#pg_upgrade#sql#database upgrade#database administration

PostgreSQL 19 enters its first release candidate on October 15, 2026. General availability is pushed to November 19, 2026. That slip is a tell. The PostgreSQL release team moved GA back by a week on October 10 to ship a second release candidate on November 12. A larger-than-usual backlog of fixes had piled up in the back branches. The project would rather delay than ship a shaky default.
That matters here because PostgreSQL 19 changes five server defaults, not zero. A clean pg_upgrade from PostgreSQL 18 does not mean a clean switch-over in production.
Test setup and methodology: we built a PostgreSQL 18.1 instance locally with initdb and no postgresql.conf overrides, then ran SHOW against it for every parameter the PostgreSQL 19 beta 4 release notes list as changed, to confirm the pre-19 baseline before trusting the documented delta. We cross-checked the release schedule against the PostgreSQL project's own beta 4 announcement rather than secondary coverage. From there we worked out which workloads each change touches, and what SQL reverses it.
Key Takeaways
- PostgreSQL 19 ships five changed server defaults.
jitgoes from on to off.max_locks_per_transactiondoubles from 64 to 128.default_toast_compressionmoves frompglztolz4.log_lock_waitsturns on by default.standard_conforming_stringsis now forced to on, with no override. - GA moved from the original October 29 target to November 19, 2026, with a second release candidate added on November 12.
pg_upgradenow actively rejects two cluster states it used to allow: object names containing a carriage return or line feed, andbtree_gistindexes oninetorcidrcolumns.
What changed, and the SQL to check each one
These are not bugs. PostgreSQL calls them behavior changes on purpose, and they will not be walked back in a later point release. Four of the five are just settings. You can flip them back at the cluster level, before or after you upgrade. The fifth one, standard_conforming_strings, has no flip. It's locked.
JIT compilation is off by default
jit moves from on in PostgreSQL 18 to off in PostgreSQL 19, according to the PostgreSQL 19 release notes. JIT (just-in-time compilation) is PostgreSQL's optional step of compiling query expressions to native machine code instead of interpreting them. We confirmed on is the current default by running show jit; against our PostgreSQL 18.1 instance with no postgresql.conf edits, which returned on.
Just-in-time compilation speeds up long analytical queries — large aggregations, big sorts, complex joins over many rows — by compiling expressions to native code instead of interpreting them row by row. It does nothing for short OLTP queries, and it adds compile-time overhead on every query that qualifies. That tradeoff is why the PostgreSQL project is flipping the default. JIT helped a narrow slice of workloads and quietly cost the rest a few milliseconds per query. There were also occasional reports of the JIT compiler itself crashing on edge-case expressions.
If your application runs reporting queries, dashboards, or anything with GROUP BY over millions of rows, upgrading silently removes a performance optimization you may be depending on. Check whether JIT is doing real work on your current instance before you upgrade:
-- Compare plan cost with and without JIT on an existing PostgreSQL 18 query
SET jit = off;
EXPLAIN ANALYZE SELECT category, count(*), avg(total)
FROM orders
GROUP BY category;
SET jit = on;
EXPLAIN ANALYZE SELECT category, count(*), avg(total)
FROM orders
GROUP BY category;
If the JIT-on plan reports noticeably lower execution time, add this to postgresql.conf after the PostgreSQL 19 upgrade:
ALTER SYSTEM SET jit = on;
SELECT pg_reload_conf();
Lock table capacity doubles
max_locks_per_transaction moves from 64 to 128, per the release notes. max_locks_per_transaction is the setting that caps how many distinct locks a single transaction can hold at once, such as when a transaction touches dozens of partitions or runs a large schema migration. This one is a size-up, not a behavior change, so it is the safest item on this list. It only affects you if you had already tuned the old default upward, to work around "out of shared memory" errors during large schema operations or transactions touching many partitions.
If your postgresql.conf sets max_locks_per_transaction = 64 explicitly (matching the old default rather than inheriting it), that override now caps you below PostgreSQL 19's own baseline. Check for an explicit override before upgrading:
Advertisement
SELECT name, setting, source
FROM pg_settings
WHERE name = 'max_locks_per_transaction';
If source reads configuration file rather than default, remove the line or raise it past 128 so the upgrade actually gives you the larger lock table rather than silently keeping the old ceiling.
TOAST compression switches from pglz to lz4
default_toast_compression moves from pglz to lz4, confirmed against the PostgreSQL 19 compression defaults section. TOAST is PostgreSQL's name for how it stores column values too large to fit in a normal row — The Oversized-Attribute Storage Technique — such as large text fields, JSON blobs, and big arrays. lz4 compresses, and especially decompresses, faster than pglz at a roughly comparable compression ratio. New TOAST data written after the upgrade benefits immediately, with no application change.
The catch is "new." This default only applies going forward. default_toast_compression controls what compression method is chosen for data written after the change takes effect. Existing TOASTed values on disk keep whatever method compressed them originally. A table with years of pglz-compressed JSON columns stays on pglz until those rows are rewritten. That rewrite can come from an UPDATE, a VACUUM FULL, or the new REPACK command PostgreSQL 19 also ships, per the beta 4 announcement. REPACK is the new command that replaces VACUUM FULL and CLUSTER, and adds a CONCURRENTLY option that rebuilds a table without blocking reads and writes — for example, reclaiming space on a table that grew large from years of updates, with no maintenance-window downtime.
If you want existing large-column data to actually pick up lz4, you have to force it per column:
ALTER TABLE events ALTER COLUMN payload SET COMPRESSION lz4;
-- Rewrite to apply the new compression to existing rows
REPACK (CONCURRENTLY) events;
Lock-wait logging is on by default
log_lock_waits moves from off to on, per Devart's PostgreSQL 19 release rundown. deadlock_timeout is the setting, 1 second by default per the PostgreSQL runtime config docs, that controls how long a session waits for a lock before PostgreSQL now logs the wait. This is unambiguously useful for diagnosing contention, and it costs nothing when there is no contention to log. The only operational impact is log volume. A database with frequent, expected lock waits — queue tables, high-concurrency row updates — will see a steady trickle of log lines that were previously silent. If your log-based alerting treats any WARNING-level line as noteworthy, budget for that before the upgrade, not after a 3am page.
standard_conforming_strings is forced on, with no escape hatch
This is the one that can actually break a pg_restore. standard_conforming_strings is the setting that controls whether a backslash inside a string literal is treated as a plain character or as an escape code. PostgreSQL has defaulted it to on since version 9.1, but it remained a settable parameter. A database could still run with it off, treating backslashes in string literals as escape characters the old way. PostgreSQL 19 removes that choice entirely. The setting is forced to on and cannot be turned off.
Here is the practical failure mode. A pg_dump taken from an old database that was still running with standard_conforming_strings = off contains backslash-escaped string literals written for that mode. Load that dump into PostgreSQL 19, and the backslashes are taken literally instead of as escapes, corrupting any string data that used them. The release notes are direct about the fix. Regenerate the dump using PostgreSQL 19's own pg_dump/pg_dumpall, from a source database already running with standard_conforming_strings = on. Do not try to load an old dump as-is.
Check every system you're planning to restore from before you touch PostgreSQL 19:
SHOW standard_conforming_strings;
If any source instance still returns off, fix that on the old server and take a fresh dump before the PostgreSQL 19 migration, not after.
What pg_upgrade will now refuse outright
Two preconditions will stop pg_upgrade from running at all, rather than warning and continuing. Catch these before migration day, not during it.
Object names with embedded line breaks. If any database, role, or tablespace name contains a carriage return or line feed character, pg_upgrade rejects the cluster. Almost nobody hits this deliberately, but it's easy to end up with by accident — a scripted CREATE DATABASE that interpolates an unsanitized string, for instance. Check for it directly:
SELECT datname FROM pg_database WHERE datname ~ E'[\r\n]';
SELECT rolname FROM pg_roles WHERE rolname ~ E'[\r\n]';
btree_gist indexes on inet or cidr columns. PostgreSQL 19 changes the default operator class for these network-address types to GiST, a change also tracked in third-party rundowns such as ComputingForGeeks' PostgreSQL 19 feature summary. The old btree_gist operator classes for the same types can silently exclude rows that should match a query. So pg_upgrade now blocks the upgrade rather than carry that correctness bug forward. Find any at-risk indexes before you schedule the migration:
SELECT indexrelid::regclass AS index_name, indrelid::regclass AS table_name
FROM pg_index i
JOIN pg_opclass oc ON oc.oid = ANY(i.indclass)
WHERE oc.opcname IN ('inet_ops', 'cidr_ops')
AND oc.opcmethod = (SELECT oid FROM pg_am WHERE amname = 'gist');
Drop and recreate any index this query surfaces using the new default opclass before attempting pg_upgrade, rather than finding out mid-migration that the tool has stopped.
The bottom line: five defaults change. One can corrupt data. Four just need a flag flipped back. Check all five before you upgrade, not after.
The upgrade checklist, in order
- Audit source instances for
standard_conforming_strings = off. Fix on the old server first; this is the only change on this list that can corrupt data on restore rather than just change performance or logging. - Run the two
pg_upgradeprecondition queries above. Both are cheap, read-only, and tell you before migration day whetherpg_upgradewill even start. - Check for an explicit
max_locks_per_transaction = 64override and remove or raise it. - Benchmark your heaviest analytical query with
jiton and off on your current PostgreSQL 18 instance. Consider, for example, your biggest nightly reporting job — and decide now whether to re-enable JIT after upgrading. - Plan a
REPACKpass (or accept that existing TOAST data stays onpglzuntil it's naturally rewritten) if storage footprint on large text/JSON columns matters to you. - Size your log pipeline for
log_lock_waits = onif your workload has routine, expected lock contention.
None of these five changes is a reason to skip PostgreSQL 19. REPACK alone is worth the upgrade on its own. It finally gives VACUUM FULL's space reclamation a non-blocking CONCURRENTLY mode, for any table that's been needing a rebuild without a maintenance window. But "defaults changed" is not a footnote here. It's the difference between a pg_upgrade that finishes and a 3am incident that starts the next morning.
FAQ
When does PostgreSQL 19 come out?
PostgreSQL 19's first release candidate is scheduled for October 15, 2026. A second release candidate follows on November 12, 2026, with general availability on November 19, 2026. These dates come from the PostgreSQL release team's October 10 update to the pgsql-hackers mailing list.
Does PostgreSQL 19 turn off JIT by default?
Yes. PostgreSQL 19 changes the jit configuration parameter's default from on to off. Workloads with long-running analytical queries that previously benefited from JIT compilation need to explicitly set jit = on after upgrading to keep that optimization.
Will my PostgreSQL dump restore cleanly into PostgreSQL 19?
Only if it was generated with standard_conforming_strings = on. PostgreSQL 19 forces this setting on with no override. A dump taken from an older database still running with it off will have its backslash-escaped strings misinterpreted on restore. Fix the setting on the source server and take a fresh dump before migrating.
What does max_locks_per_transaction default to in PostgreSQL 19?
128, up from 64 in PostgreSQL 18 and earlier. If your postgresql.conf explicitly sets it to 64, that override now holds you below the new baseline until you remove or raise it.
Does pg_upgrade still work with btree_gist indexes on inet columns?
Not automatically. PostgreSQL 19's pg_upgrade rejects clusters containing btree_gist indexes on inet or cidr columns. The default operator class for those types changed to GiST, and the old opclasses can return incorrect query results. Drop and recreate the index with the current default opclass before upgrading.
Advertisement
FAQ
When does PostgreSQL 19 come out?
PostgreSQL 19's first release candidate is scheduled for October 15, 2026. A second release candidate follows on November 12, 2026, with general availability on November 19, 2026, per the PostgreSQL release team's October 10 update to the pgsql-hackers mailing list.
Does PostgreSQL 19 turn off JIT by default?
Yes. PostgreSQL 19 changes the jit configuration parameter's default from on to off. Workloads with long-running analytical queries that previously benefited from JIT compilation need to explicitly set jit = on after upgrading to keep that optimization.
Will my PostgreSQL dump restore cleanly into PostgreSQL 19?
Only if it was generated with standard_conforming_strings = on. PostgreSQL 19 forces this setting on with no override, so a dump taken from an older database still running with it off will have its backslash-escaped strings misinterpreted on restore.
What does max_locks_per_transaction default to in PostgreSQL 19?
128, up from 64 in PostgreSQL 18 and earlier. If your postgresql.conf explicitly sets it to 64, that override now holds you below the new baseline until you remove or raise it.
Does pg_upgrade still work with btree_gist indexes on inet columns?
Not automatically. PostgreSQL 19's pg_upgrade rejects clusters containing btree_gist indexes on inet or cidr columns, because the default operator class for those types changed to GiST and the old opclasses can return incorrect query results.
Comments
Loading…
Sign in to join the conversation.
Related posts

Prisma 8 Is Still Breaking Between RCs. Wait.
Methodology: every breaking change listed below comes from Prisma's own release notes for that specific release-candidate tag on GitHub, not a secondary summary. We read rc.8 through rc.17
Wed Oct 14 2026 · 7 min read · 0 views

NestJS Redis caching: cache-manager traps, measured
NestJS Redis caching with cache-manager, tested on Nest 12. Six traps we measured, from leaked user data to a silent wrap failure, plus the fixes.
Tue Oct 13 2026 · 10 min read · 0 views

NestJS JWT auth: Passport, guards and the new package
NestJS authentication with Passport, JWT and guards, tested on Nest 12. Four traps we measured, plus the new @nestjs/authentication package.
Tue Oct 13 2026 · 13 min read · 0 views