PostgreSQL 19 Release Guide: Logical Replication, WAL Level and Upgrade Plan
The most-discussed partitioning feature of the PostgreSQL 19 cycle is not in PostgreSQL 19. ALTER TABLE ... MERGE PARTITIONS and SPLIT PARTITION were committed in December 2025, then reverted on 26 August 2026 because of design problems the committers judged too large to fix before release. If your upgrade plan assumed you could finally consolidate a decade of monthly partitions in place, that line item needs to come out. What the release does contain is arguably more valuable for anyone running a fleet: a logical replication stack that finally moves sequences and no longer needs a restart to switch on, a dynamic WAL level, and a clutch of monitoring views.
This matters now because PostgreSQL 19 is in beta (Beta 4 shipped on 24 September 2026), the project’s open-items page lists a planned general availability date of 29 October 2026, and upgrade rehearsals take weeks, not days. The right time to find out which of your extensions, dumps and monitoring queries break is before the release candidate, not after.
You will leave with a verified inventory of what is in and out, the breaking changes that will bite real clusters, and a concrete upgrade runbook with SQL for both pg_upgrade and the logical-replication blue/green route.
What this covers: the corrected status of partition merge and split, the logical replication and WAL changes, new observability, the incompatibility list, two upgrade paths with a decision matrix, failure modes, and a rehearsal checklist.
Context and Background
PostgreSQL ships one major version a year, normally in the autumn, and supports each for five years. Between majors the project publishes four or five betas and one or more release candidates; production use of a beta is explicitly discouraged. Everything in this article is drawn from the PostgreSQL 19 release notes, which at the time of writing carry the banner “Release date: 2026-??-??, AS OF 2026-09-14”. That means the notes are a draft: individual entries can still change, and I flag where that risk is highest.
The previous major, PostgreSQL 18, reshaped the I/O path (asynchronous I/O) and made upgrades gentler by letting pg_upgrade carry optimizer statistics across. PostgreSQL 19 continues in two directions. First, operability: a unified REPACK command, parallel autovacuum workers, and a scoring system that decides which tables autovacuum visits first. We cover the repack story separately in our REPACK versus pg_repack versus VACUUM FULL comparison, so this post stays on replication, upgrades and monitoring. Second, logical replication: every release since 10 has widened what it can carry, and 19 closes two long-standing gaps, sequences and the restart requirement.
For industrial and IoT teams the relevance is direct. Telemetry stores built on PostgreSQL with native partitioning or TimescaleDB, such as the ones discussed in our PostgreSQL 18 versus TimescaleDB versus ClickHouse comparison for IoT, are exactly the clusters where a major upgrade is hard: multi-terabyte tables, tight maintenance windows, and downstream consumers reading logical decoding streams. The upgrade techniques below are chosen with that profile in mind.
One framing point about sources. The release notes are the primary reference. Where I cite the commit history, the open-items wiki, or news coverage, I say so. Nothing here is a benchmark; the worked arithmetic is labelled illustrative and exists to show how to size a rehearsal, not to promise a result.
What Is Actually in PostgreSQL 19, and What Is Not
PostgreSQL 19 adds sequence replication to logical replication, lets logical decoding switch on at runtime when wal_level is replica (reported through effective_wal_level), introduces REPACK, parallel autovacuum and a WAIT command for standbys. It does not include partition merge or split, which was reverted in August 2026 and is deferred to a later release.

Figure 1: Where PostgreSQL 19 changes land across the replication, WAL, vacuum and monitoring layers, with the reverted partition feature shown outside the release.
Figure 1 groups the release by the layer an operator touches. The replication column is where upgrade planning pays off, the WAL column changes how you provision, and the monitoring column changes what dashboards you can build without extensions.
The partition merge and split reversal
The history is worth knowing because it tells you how to treat the feature when it returns. Dmitry Koval’s patch to merge and split partitions was first committed for PostgreSQL 17 and reverted before that release. It was committed again on 14 December 2025 (depesz documented the merge and split commits in its “Waiting for PostgreSQL 19” series). Then, per the commit message posted to the pgsql-committers list on 26 August 2026, Alexander Korotkov reverted the whole thing: 13 commits, roughly 9,000 lines across 29 files, with the stated reason that the feature had “multiple design issues which are too late to address in this release cycle”. Postgres Weekly reported that the release team had requested the removal.
The rejected design is instructive. The December 2025 version took an ACCESS EXCLUSIVE lock on the parent table for the entire operation, including tuple routing, and ran in a single process. A write-up on The Build blog argued that this was a deliberate trade of speed for correctness after the PostgreSQL 17 attempt, which tried finer-grained locking, ran into deadlock hazards with concurrent partition-descriptor traversal, and was pulled. Even the conservative design did not survive the 19 cycle. The practical lesson: do not plan any production workflow around an in-core online merge or split before at least PostgreSQL 20, and keep your current tooling.
What do you use instead? The proven pattern is attach and detach. To merge two monthly partitions you create a new table with the combined bounds, copy or move rows in batches, then DETACH PARTITION ... CONCURRENTLY the old ones and ATTACH PARTITION the new one. To split a default or overflow partition, you create the new children, move rows with batched INSERT ... SELECT and DELETE, and attach with a pre-validated check constraint so the attach scan is skipped. Extensions such as pg_partman automate this. None of that changed in 19, and the one partition-related item that did ship is small but useful: COPY TO can now read directly from a partitioned table, and vacuumdb --analyze-only and --analyze-in-stages now include partitioned parents, which matters because a partitioned parent has no statistics until it is analyzed explicitly.
Logical replication: sequences, exclusions, and retained dead tuples
Logical replication has always carried table rows but never sequence state. Every logical upgrade or migration therefore ended with a script that read each sequence on the old primary and ran setval on the new one during the cutover window, and a forgotten sequence meant duplicate-key errors the minute writes resumed. PostgreSQL 19 addresses this directly. A publication can now be created with FOR ALL SEQUENCES, and sequence values on the subscriber are synchronised during CREATE SUBSCRIPTION, ALTER SUBSCRIPTION ... REFRESH PUBLICATION, and the new ALTER SUBSCRIPTION ... REFRESH SEQUENCES. The last of those updates values only, not the existence of sequences; to pick up new sequences you refresh the publication. The new function pg_get_sequence_data() lets you inspect synchronisation, and the release notes also record that it now returns the sequence page LSN.
Two further publication features reduce boilerplate. The new EXCEPT clause, aimed at publications defined with ALL TABLES, lets you publish everything except a named set, which is the natural way to leave out scratch, audit-staging or high-churn tables. And subscriptions gain retain_dead_tuples, which keeps the information needed for conflict detection, bounded by max_retention_duration so a stalled subscriber cannot hold back cleanup forever. Per the release notes, pg_stat_subscription_stats gets an update_deleted counter (rows where an update was ignored due to a concurrent delete) that requires retain_dead_tuples on the subscriber.
Subscriptions can also take their connection parameters from a foreign server defined with postgres_fdw using CREATE SUBSCRIPTION ... SERVER, which centralises credentials and lets you manage them with ordinary USER MAPPING privileges rather than embedding passwords in connection strings. And wal_receiver_timeout can now be set per subscription and per user.
Dynamic WAL level: effective_wal_level
Until now, logical decoding required wal_level = logical set at server start. Since changing it needs a restart, shops running replica for lower WAL volume faced an unpleasant choice: restart a busy primary just to create a first publication or logical slot, or pay the WAL overhead of logical everywhere forever. The release notes now say that when wal_level is replica, logical replication can be enabled automatically when needed (credited to Masahiko Sawada), and a new server variable effective_wal_level, plus pg_controldata and pg_control_checkpoint(), report what the cluster is actually running at.

Figure 2: The configured wal_level stays replica while effective_wal_level rises to logical once a logical slot or subscription needs it, with no restart.
The release notes do not spell out the internal mechanism, so treat the following as the operational reading rather than a specification. The configured wal_level is your intent. The effective level is what the instance is doing right now. Creating the first logical slot is the trigger for the effective level to rise, and the practical consequence is that your monitoring should alert on effective_wal_level, not on wal_level. If a capacity plan assumed replica-sized WAL and someone creates a subscription, WAL volume can grow without any configuration change appearing in pg_settings. Check the documentation for exactly when the level drops back after the last slot is removed before you rely on that behaviour.
For upgrade planning the benefit is concrete. In the logical blue/green method, the old cluster must be able to decode. On PostgreSQL 17 and earlier, that is a restart in the weeks before cutover if you were not already on logical. The dynamic level is a PostgreSQL 19 server feature, and the source of your first migration is still your old version, so it will not rescue this upgrade; it helps the next one. Many teams should therefore set wal_level = logical on their current production now, as a separate low-risk change, so the 19 cutover has no restart on the critical path.
Standbys and failover plumbing
Three streaming-replication items affect operations. A new WAIT command lets a session on a standby wait until a given LSN has been written, flushed or replayed, supporting read-your-writes behaviour: the application captures pg_current_wal_lsn() after a commit on the primary and issues a wait on the replica before reading. pg_sync_replication_slots() now waits for synchronisation to complete and reports failures that were previously silent. And wal_sender_shutdown_timeout bounds how long a sender waits for replicas to catch up during shutdown; by default senders still wait indefinitely, which is the safe behaviour and also the reason a stuck replica can make a primary shutdown hang during a maintenance window.
Observability: New Views and Columns You Can Use on Day One
The monitoring changes are numerous and mostly additive, which makes them the cheapest wins in the release. Per the release notes, new system views are pg_stat_lock (per lock type statistics), pg_stat_recovery (recovery status), and pg_stat_autovacuum_scores (per-table autovacuum prioritisation detail). pg_get_multixact_stats() reports multixact activity, and a pg_dsm_registry_allocations view exposes dynamic shared memory registrations.
Existing views gain columns that matter for replication health. pg_stat_replication_slots and pg_replication_slots get slotsync_skip_count, slotsync_last_skip and slotsync_skip_reason, which finally answer “why did my failover slot not sync” without reading logs. pg_stat_replication_slots also reports how often logical_decoding_work_mem was exceeded (mem_exceeded_count), which is the signal that decoding is spilling to disk and your slot lag is about to grow. pg_stat_subscription_stats gains sync_seq_error_count and update_deleted.
A note on a correction. The slot brief for this post mentioned replication lag tracking. I found no new lag column or view in the release notes. Replication lag is still observed the way it has been: write_lag, flush_lag and replay_lag in pg_stat_replication, and LSN differences for slots. What 19 adds is the slot-sync skip data, the decoding memory counter, and WAIT for application-level lag handling. If you read a different claim elsewhere, check it against the release notes before building an alert on a column that may not exist.
Other observability changes worth enabling: log_lock_waits is now on by default; wraparound warnings for transaction and multixact IDs start at 100 million remaining instead of 40 million, giving operators more time; stats_reset columns appear on pg_stat_all_tables, pg_stat_all_indexes, pg_statio_all_sequences and others, so you can tell how long counters have accumulated; EXPLAIN (ANALYZE, IO) reports asynchronous I/O activity; and log_min_messages can be set per process type using a type:level form.
Parallel autovacuum and scoring
Autovacuum can now use parallel workers to vacuum a table’s indexes, capped by autovacuum_max_parallel_workers and a per-table autovacuum_parallel_workers storage parameter. A new scoring model, tunable by autovacuum_freeze_score_weight, autovacuum_multixact_freeze_score_weight, autovacuum_vacuum_score_weight, autovacuum_vacuum_insert_score_weight and autovacuum_analyze_score_weight, decides which tables are processed first, and pg_stat_autovacuum_scores lets you see the ranking. For partitioned telemetry stores with hundreds of children, that ordering matters: freeze-age emergencies can now outrank routine analyze work by weight rather than by luck. Treat the defaults as a starting point and verify behaviour under your own write pattern; I have no published benchmark to cite for the gain.
Breaking Changes That Will Bite Real Clusters
The release notes’ “Migration to Version 19” section is short but sharp. Read it as a test plan.
| Change | Who is hit | What to do |
|---|---|---|
standard_conforming_strings is always on; escape_string_warning removed |
Dumps taken with it off, legacy apps using backslash escapes in plain string literals | Re-dump with 19 tools or fix the app; audit string literals |
inet and cidr default operator class changed from btree_gist to GiST; pg_upgrade refuses clusters with such btree_gist indexes |
Exclusion constraints and GiST indexes on network types | Drop and recreate the indexes before the upgrade |
Carriage returns and line feeds banned in database, role and tablespace names; pg_upgrade refuses them |
Rare, but fatal to the upgrade if present | Rename objects on the old cluster |
max_locks_per_transaction default raised from 64 to 128 |
Explicit settings that were tuned to the old lock table size | Re-derive the value; the notes say lock sizing changed |
| JIT is off by default | Analytical queries that relied on it | Enable per role or database after measuring |
json_array() returns [] instead of NULL for an empty result |
Code that tests for NULL | Review call sites |
pg_stat_subscription_stats.sync_error_count renamed sync_table_error_count; wait event type BUFFERPIN renamed BUFFER |
Dashboards and alert rules | Update queries |
MULE_INTERNAL encoding removed; RADIUS authentication removed |
Databases in that encoding; RADIUS users | Re-encode via dump and restore; move to another auth method |
COPY FROM ... WHERE rejects system columns |
Odd ETL filters | Rewrite the filter |
postgres_fdw now propagates READ ONLY and DEFERRABLE transaction status |
Read-only transactions that wrote through foreign tables | Move the write path |
Two of these deserve extra comment. The default TOAST compression also changes from pglz to lz4, per the release notes: it only affects newly written values, so storage and CPU profiles drift gradually after the upgrade rather than changing on day one. And MD5 password authentication now logs a warning after a successful login (controlled by md5_password_warnings), with a new password_expiration_warning_threshold defaulting to seven days. MD5 was already marked deprecated in 18, so migrate role passwords to SCRAM before the upgrade; you can do that on the old version and it removes a whole class of noise afterwards.
Upgrade Paths: pg_upgrade Versus Logical Blue/Green
There are two sane ways to reach PostgreSQL 19 from an earlier major, and the release notes name both: dump and restore, pg_upgrade, or logical replication. Dump and restore is only reasonable for small databases. For everything else the choice is between an in-place pg_upgrade, with downtime proportional to catalog size and the file-transfer mode, and a logical blue/green migration with downtime proportional to your cutover discipline.

Figure 3: A decision flow for choosing pg_upgrade or a logical blue/green migration based on tolerable downtime, extension support and rollback needs.
Path A: pg_upgrade
pg_upgrade rewrites the system catalogs and reuses or transfers the data files. In its default copy mode it duplicates every data file, so downtime scales with database size and you need roughly double the disk. In --link mode it hard-links files, making the cutover fast but removing the old cluster as a clean rollback once the new one has started. --clone uses file-system reflinks where supported, and PostgreSQL 18 added --swap, which moves directories rather than copying. Check the 19 documentation for the flags available in your build, and rehearse on a snapshot rather than guessing.
PostgreSQL 19 contributes several pg_upgrade refinements per the release notes: faster copying of large object metadata, support for non-default tablespaces located inside PGDATA (previously an error), and the refusals listed earlier for CR/LF names and btree_gist inet/cidr indexes. Separately, pg_dump can now include restorable extended statistics through pg_restore_extended_stats(), so a dump-and-restore migration no longer leaves multi-column planner statistics empty until the next analyze. Combine that with vacuumdb --analyze-in-stages, which now covers partitioned parents, and the post-upgrade statistics gap shrinks even on the fallback path.
A skeleton run looks like this. Treat the paths and the --jobs value as placeholders.
# 1. Install 19 binaries beside 17 or 18. Initialise the new cluster with matching options.
/usr/lib/postgresql/19/bin/initdb -D /pgdata/19 --data-checksums
# 2. Dry run: validates compatibility, including the new btree_gist and name checks.
/usr/lib/postgresql/19/bin/pg_upgrade \
--old-bindir=/usr/lib/postgresql/17/bin \
--new-bindir=/usr/lib/postgresql/19/bin \
--old-datadir=/pgdata/17 --new-datadir=/pgdata/19 \
--jobs=8 --link --check
# 3. In the maintenance window: stop the old server, then run the same command without --check.
# 4. Start 19, then rebuild statistics in stages so queries get usable plans quickly.
vacuumdb --all --analyze-in-stages --missing-stats-only
Match the checksum setting between old and new clusters; pg_upgrade requires it. The --missing-stats-only flag exists from PostgreSQL 18, where pg_upgrade began preserving optimizer statistics; confirm your source version retained them, because upgrading from 16 or earlier still starts with empty statistics.
Path B: logical replication blue/green
The logical route builds a new PostgreSQL 19 cluster, replicates into it while production keeps running, and cuts over when lag is near zero. Its advantages are a rehearsable, repeatable process, a rollback path (keep the old primary, optionally reverse-replicate), and downtime measured in seconds to a couple of minutes. Its costs are real: every table needs a replica identity, DDL is not replicated, large objects are not replicated, and until now sequences were a manual step.

Figure 4: The cutover sequence from initial table sync to sequence refresh, write freeze, lag drain and application switch.
The 19 additions change the shape of the runbook in two places. Sequence synchronisation moves from a hand-rolled script into the product, and pg_createsubscriber (which converts a physical standby into a logical subscriber) can now ignore publications that already exist and takes a --logdir option. Here is the core of the process.
-- On the OLD primary (17 or 18). Needs wal_level = logical already in place.
CREATE PUBLICATION upg_pub FOR ALL TABLES;
-- On 19 you can also publish sequences. Check the CREATE PUBLICATION page of the
-- 19 documentation for the exact ALL SEQUENCES and EXCEPT syntax before scripting it.
-- On the NEW 19 cluster, after restoring the schema only (pg_dump --schema-only | psql):
CREATE SUBSCRIPTION upg_sub
CONNECTION 'host=old-primary dbname=app user=repl_upg'
PUBLICATION upg_pub
WITH (copy_data = true);
-- Watch initial sync: every table should reach state r (ready).
SELECT srrelid::regclass AS tbl, srsubstate
FROM pg_subscription_rel
WHERE srsubstate <> 'r';
During the initial copy the publisher must retain WAL for the slot. A slow or stalled subscriber makes the old primary’s disk grow, so cap it with max_slot_wal_keep_size on the old cluster and alert on pg_replication_slots.wal_status and safe_wal_size. Both of those exist today and need no 19 features.
At cutover time the sequence is: pause writes at the application or at the pooler, wait for the subscription to drain, refresh sequences, then switch connections.
-- 1. Freeze writes on the old primary (pooler pause, or revoke write privileges briefly).
-- 2. Capture the publisher position and wait for the subscriber to reach it.
SELECT pg_current_wal_lsn(); -- run on old primary, note the value
SELECT received_lsn, latest_end_lsn -- run on new cluster, repeat until caught up
FROM pg_stat_subscription WHERE subname = 'upg_sub';
-- 3. On 19, synchronise sequence values from the publisher.
ALTER SUBSCRIPTION upg_sub REFRESH SEQUENCES;
-- 4. Verify one or two critical sequences, then point the application at 19.
SELECT * FROM pg_get_sequence_data('public.orders_id_seq');
If your source is PostgreSQL 17 or 18, sequence replication is a feature of the 19 subscriber plus the publisher’s ability to publish sequences, so confirm in the documentation whether a pre-19 publisher can serve REFRESH SEQUENCES. I could not verify that compatibility from the release notes alone. If it cannot, keep the classic fallback: generate setval statements from the old primary, add a safety margin to each value, and apply them during the freeze. The margin matters. A gap in sequence values is harmless; a duplicate key at 02:00 on a Sunday is not.
Sizing a rehearsal (illustrative arithmetic)
Rehearsal is where upgrades are won. Suppose a 6 TB database with 40 MB/s of sustained read throughput available on the source for the initial copy. The raw transfer is 6,000,000 MB divided by 40 MB/s, or 150,000 seconds, about 42 hours. These figures are illustrative, not measured. Index builds on the subscriber add to that, and parallel table sync workers, governed by max_sync_workers_per_subscription, cut the elapsed time only if source I/O and network are not the bottleneck. During those 42 hours, if the source generates 15 GB of WAL per hour, the slot pins about 630 GB of WAL. Provision for that before you start, not after the disk alarm.
For pg_upgrade --link, the equivalent estimate is dominated by the catalog rewrite and, for databases with very many objects, the per-object dump and restore of schema. Time it on a restored snapshot: that single number, not a vendor claim, is your real downtime for Path A.
| Dimension | pg_upgrade –link | Logical blue/green | Dump and restore |
|---|---|---|---|
| Downtime | Minutes, grows with object count | Seconds to minutes if disciplined | Hours to days |
| Rollback | Weak once new cluster runs | Strong, old primary intact | Strong |
| Extra disk | Minimal | Full second copy plus WAL retention | Full second copy |
| DDL during migration | Not applicable | Frozen or manually applied | Not applicable |
| Sequences | Preserved | Manual before 19, built-in refresh on 19 | Preserved by dump |
| Large objects | Preserved | Not replicated | Preserved |
| Skill and tooling load | Low | High | Low |
Trade-offs, Gotchas, and What Goes Wrong
Betting the plan on a beta feature list. The release notes are marked as a draft, and the partition merge and split episode shows that a feature can disappear even after it was committed and documented, as late as the weeks around Beta 3 and Beta 4. Build your plan from the final release notes and the release candidate, and keep every dependence on a new feature behind a feature gate in your own runbook.
Replica identity gaps. Logical replication cannot replicate UPDATE and DELETE for a table that has no primary key and no replica identity. Tables that pass quietly in a physical world, such as staging or log tables, stop the subscriber with an error once changes arrive. Audit pg_class.relreplident and primary keys across every schema before the first rehearsal.
DDL drift during the copy window. Logical replication does not carry schema changes. A migration that runs for days while developers keep shipping ALTER TABLE will break the subscription on the first column mismatch. Freeze schema changes, or script every DDL change to be applied on both sides in a defined order, with the subscriber getting additive changes first.
Slot-induced disk exhaustion. A subscription that cannot keep up pins WAL on the publisher. This is the classic way a logical migration takes down the production primary rather than the new one. Set max_slot_wal_keep_size, accept that an invalidated slot means restarting the sync, and prefer that outcome to a full disk.
Conflicts after cutover rehearsal. If you test cutover by writing on both clusters, you will create conflicts that stall apply. The new retain_dead_tuples option improves detection for update-versus-delete conflicts but needs retained dead tuples on the subscriber, which costs bloat; the max_retention_duration cap exists precisely so the bloat is bounded. During a one-way migration you normally do not need either, so leave them off unless you run bidirectional replication.
Monitoring that silently breaks. The renamed column sync_table_error_count and the renamed wait event type BUFFER will return errors or empty series in alert queries. A dashboard that silently shows no data looks identical to a healthy database. Grep every monitoring repository for sync_error_count and BUFFERPIN now.
Extensions. Every extension must ship a build for 19. Hooks and internals changed: get_relation_info_hook was removed and build_simple_rel_hook added, index access method handlers moved to a static IndexAmRoutines, and C11 is now required to build. Any extension with custom index access methods or planner hooks, which includes some time-series and graph extensions, needs a vendor statement for 19 before you commit to a date.
Default changes that move performance. JIT off, TOAST compression default lz4, log_lock_waits on, and a doubled max_locks_per_transaction all change behaviour without any DDL. None is dangerous alone; together they can make a before-and-after comparison confusing. Capture a baseline workload on the old version, then change one default at a time on the new one.
Partitioned parent statistics. Because a partitioned parent has no statistics until analyzed, a plan that references the parent can regress after an upgrade that restored only leaf statistics. The 19 vacuumdb change helps, but verify with pg_stats on a representative parent.
Practical Recommendations
Treat the upgrade as three separate projects that happen to share a date: clearing blockers on the old cluster, rehearsing the move, and adopting new features afterwards. The first can start today and carries no dependence on the final release.
Do not adopt features on cutover day. Move to 19 with the same schema, same settings where possible, and same extension set. Adopt REPACK, parallel autovacuum and the new monitoring views in the following weeks, one at a time, so a regression has an obvious cause. Keep the old primary intact for at least a full business cycle: month-end jobs are where hidden incompatibilities live.
Choose the route by downtime tolerance. If a 10 to 30 minute window is acceptable and the catalog is moderate in size, pg_upgrade --link with a verified storage snapshot is simpler and has fewer failure modes. If you need near-zero downtime or a reliable rollback, plan the logical route, and budget engineering time for replica identity cleanup and DDL freeze discipline.
Checklist to run now, on your current production version:
- Set
wal_level = logicalin a planned restart if you intend a logical migration; confirm withSHOW wal_level. - Find
inetandcidrindexes usingbtree_gistand plan to recreate them. - Search for names with CR or LF characters in databases, roles and tablespaces.
- Migrate MD5 role passwords to SCRAM and confirm
password_encryption. - Grep dashboards for
sync_error_countandBUFFERPIN. - Verify every table has a primary key or explicit replica identity.
- Confirm each extension has a vendor-tested 19 build.
- Re-dump a staging copy with
standard_conforming_strings = onand confirm it restores. - Record baseline query timings, plans and WAL volume for the rehearsal comparison.
- Rehearse the full runbook on a restored snapshot and time each stage.
If partition consolidation is on your list, do it with attach and detach on your current version, or leave it. Do not wait for an in-core command. Our write-up on TimescaleDB hypertables, chunks and compression covers the alternative for time-series workloads where chunk management is automated.
Frequently Asked Questions
When will PostgreSQL 19 be released?
At the time of writing PostgreSQL 19 is in beta; Beta 4 was announced on 24 September 2026, and the project’s open-items wiki lists a planned general availability date of 29 October 2026. That is a plan, not a commitment, and release candidates come first. Re-check the postgresql.org news page before scheduling anything against the date, and do not run a beta in production.
Does PostgreSQL 19 support merging and splitting partitions?
No. The ALTER TABLE ... MERGE PARTITIONS and SPLIT PARTITION commands were committed in December 2025 and reverted on 26 August 2026 because of design issues too large to fix in the cycle, after an earlier revert in PostgreSQL 17. Use attach and detach, batched row moves, or pg_partman to consolidate or divide partitions, and watch the PostgreSQL 20 development cycle for another attempt.
Can I turn on logical replication in PostgreSQL 19 without a restart?
Per the release notes, when wal_level is replica, logical replication can be enabled automatically when needed, and effective_wal_level reports the level in force. Your source cluster in a first migration is the old version, which lacks this, so set wal_level = logical on it ahead of time. Confirm in the documentation when the effective level falls back after slots are dropped.
Are sequences replicated in PostgreSQL 19?
Yes, with limits. Publications can include sequences with the ALL SEQUENCES clause, and subscribers synchronise values at subscription creation, on REFRESH PUBLICATION, and on ALTER SUBSCRIPTION ... REFRESH SEQUENCES, which updates values only and not sequence existence. Sequence values are synchronised on demand rather than streamed, so refresh them during the cutover freeze, then verify with pg_get_sequence_data().
Should I use pg_upgrade or logical replication to reach PostgreSQL 19?
Use pg_upgrade --link when a short outage is acceptable and rollback via storage snapshot is enough; it is simpler and has fewer moving parts. Use logical replication when you need near-zero downtime or a clean fallback to the old primary, accepting the work of replica identities, DDL freezing and WAL retention. Either way, rehearse on a snapshot and time each stage.
What breaks when upgrading to PostgreSQL 19?
The main compatibility items are forced standard_conforming_strings, the inet and cidr btree_gist operator class change that blocks pg_upgrade, banned CR and LF characters in object names, JIT off by default, the doubled max_locks_per_transaction default, and renamed monitoring columns and wait events. RADIUS authentication and the MULE_INTERNAL encoding are removed. Extensions with planner or index hooks need rebuilds.
Further Reading
- PostgreSQL 19 REPACK versus pg_repack versus VACUUM FULL for the table reorganisation feature that ships alongside this release.
- PostgreSQL 18 versus TimescaleDB versus ClickHouse for IoT for choosing where telemetry should live before you plan the upgrade.
- Multi-region active-active database architecture for the conflict and consistency issues that bidirectional logical replication raises.
- PostgreSQL versus CockroachDB and YugabyteDB: an ADR if the upgrade is also a moment to question the single-primary model.
- Apache Spark 4.2 versus 4.1 for CDC and geospatial workloads for downstream consumers of logical decoding and change streams.
- PostgreSQL 19 release notes, the primary reference for every item above.
- PostgreSQL 19 open items wiki, where the planned release date and the partition-feature resolution are recorded.
- Postgres Weekly issue 663, reporting the removal of partition merge and split.
By Riju — about
