Query icon
Fail icon
Log icon
If icon
Queries icon
Commands icon
Docker icon
LoopUntil icon
Pause icon
SlackIncomingWebhook icon
Schedule icon

Upgrade PostgreSQL to a new major version with a verified cutover

Upgrade PostgreSQL to a new major version with logical replication. Freeze the old server, catch up, compare every table and cut over with a recorded result.

Categories
Data

A dump and restore needs a long outage and an in-place upgrade has no way back. Logical replication lets a newer server catch up while the old one keeps serving, but the last steps are easy to get wrong: stopping writers, waiting for the exact catch-up point, copying sequences and proving that nothing is missing. This flow runs those steps in order and records the result.

How it works

PREPARE checks the source (wal_level logical, a key on every table), copies the schema with pg_dump in a single transaction, creates a publication and a subscription and waits until every table is in the ready state.

CUTOVER asks for approval through a Pause. It makes the source database read only, ends the other sessions and verifies that no role overrides the setting. It reads the WAL position and loops until the replication slot confirmed that position and a count plus an MD5 sum of every row match on both servers. Then it copies every sequence value and records COMPLETED.

ROLLBACK reopens the source. Replication keeps running, so CUTOVER can be tried again. FINALIZE drops the subscription, the slot and the publication after approval.

What you get

A target database that matches the source at a recorded WAL position. Sequences continue where the old ones stopped. A kestra_upgrade.upgrade_log table shows each state. A failed cutover leaves the source read only on purpose.

Who it's for

Platform teams that upgrade a PostgreSQL major version and want a short, reversible outage.

Why orchestrate this with Kestra

Pause holds the approval. LoopUntil waits for the catch-up and the comparison. The log table makes every mode safe to run again after a failure.

Tested end to end

Kestra 2.0.6 and 2.0.5 with PostgreSQL 16.15 as the source and 17.5 as the target. Writers ran during the initial copy and during the cutover. A prepare that failed halfway resumed without copying again. Declined and stale approvals, a role setting that kept one role writable, a table that no longer matched, rollback and finalize were exercised. Each result was compared with psql.

Prerequisites

wal_level logical on the source. A superuser for both servers. An empty target database. Docker workers for pg_dump. The same roles and extensions on the target. The flow copies tables and sequences only.

Secrets

PG_SOURCE_PASSWORD, PG_TARGET_PASSWORD and SLACK_WEBHOOK_URL.

Inputs

mode selects PREPARE, CUTOVER, ROLLBACK or FINALIZE. dry_run defaults to true and auto_remediate to false, so nothing changes until both are flipped. The subscription connection string contains the source password, so use one without spaces or quotes.

Quick start

  1. Set wal_level to logical on the source and restart it. Create the roles on the target.
  2. Run PREPARE as a dry run and read the plan. Run it again with dry_run false and auto_remediate true.
  3. Stop the applications. Run CUTOVER and approve it. Repoint the applications when it reports COMPLETED.
  4. Run FINALIZE when the old server is no longer needed.

How to extend

Replace the publication with a table list to upgrade part of a database. Add a smoke test of the new server before the approval. Large tables make the comparison slow, so sample them. Large objects and DDL during the window are not replicated.

Links

This blueprint was created by zkasuran.

See How

New to Kestra?

Use blueprints to kickstart your first workflows.