Skip to main content
Back to All Whitepapers/AWS & Cloud Architecture
AWS & Cloud ArchitectureMarch 5, 20262 min read

Zero-Downtime Database Migrations: Lessons from Production

Step-by-step methodology for executing cross-account AWS RDS and multi-cloud database cutovers with 0% data loss.

SA

Sikander Ali

Senior DevOps Architect

Executive Engineering Summary & Takeaways

  • Logical replication and continuous Change Data Capture (CDC) keep destination databases synchronized in real time prior to cutover.
  • Decoupling schema migrations into backward-compatible expand-and-contract phases prevents application query failures.
  • PgBouncer connection pooling and traffic pausing reduce cutover write freezes to under 5 seconds.
  • Automated pre-cutover data validation using row count hashes and primary key diff scripts guarantees zero data loss.
  • DNS TTL reduction to 60 seconds 48 hours prior to maintenance ensures instantaneous global routing during switchover.

1. The Multi-Phase Zero-Downtime Migration Architecture

Migrating production transactional databases between AWS accounts, VPCs, or cloud providers under active customer load carries high risk. Naive approaches requiring extended maintenance windows cause severe revenue loss and SLA breaches.

Our production migration blueprint relies on a 5-stage protocol: Schema Baseline Initialization, Continuous Logical Replication (CDC), Real-time Consistency Verification, Microsecond Traffic Drain via PgBouncer, and Reverse Replication Failback.

2. Setting Up PostgreSQL Logical Replication (pgoutput)

PostgreSQL logical replication uses a publish-and-subscribe model based on WAL decoding. Unlike physical streaming replication, logical replication allows data transfer across different PostgreSQL major versions and cloud providers.

Ensure wal_level = logical and configure appropriate max_replication_slots on the source instance before creating the publication.

postgres_logical_replication.sql
SQL
-- On Source Database (Publisher)
ALTER SYSTEM SET wal_level = 'logical';

CREATE PUBLICATION prod_migration_pub 
FOR ALL TABLES;

-- On Destination Database (Subscriber)
CREATE SUBSCRIPTION prod_migration_sub
CONNECTION 'host=source-db.technofreaks.internal port=5432 dbname=production user=replicator password=REDACTED sslmode=require'
PUBLICATION prod_migration_pub
WITH (copy_data = true, create_slot = true);
Important Notice

Replication Slot Disk Guardrail: Always monitor wal_keep_size and replication slot lag. If the subscriber disconnects, the publisher retains WAL logs indefinitely, which can exhaust disk storage.

3. Expand and Contract Schema Migrations

Never execute breaking schema modifications (renaming columns, changing data types, dropping constraints) in a single step. Follow the Expand and Contract pattern:

1. Expand: Add the new column or table as nullable. Deploy application code that writes to both old and new columns.

2. Backfill: Migrate historical records in batches to populate the new column.

3. Contract: Deploy application code that reads only from the new column, then safely drop the legacy column in a subsequent release.

4. PgBouncer Traffic Pausing & Microsecond Cutover

During the final cutover, rather than restarting application servers or waiting for DNS to propagate, PgBouncer is used to pause incoming client connections.

Issuing PAUSE <database> holds incoming TCP connections in a pending queue while allowing in-flight queries to finish. The replication lag is verified to be 0 bytes, connection strings are updated to point to the new destination, and RESUME <database> is executed.

pgbouncer_cutover.sh
Bash
#!/usr/bin/env bash
set -euo pipefail

echo "==> Step 1: Pausing incoming database writes on PgBouncer..."
psql -h 127.0.0.1 -p 6432 -U admin pgbouncer -c "PAUSE production_db;"

echo "==> Step 2: Verifying logical replication lag is exactly 0 bytes..."
LAG=$(psql -h source-db -U admin -t -c "SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn) FROM pg_replication_slots WHERE slot_name='prod_migration_sub';")
echo "Replication Lag: ${LAG} bytes"

echo "==> Step 3: Updating PgBouncer target database host to Destination..."
sed -i 's/source-db.technofreaks.internal/dest-db.technofreaks.internal/g' /etc/pgbouncer/pgbouncer.ini
psql -h 127.0.0.1 -p 6432 -U admin pgbouncer -c "RELOAD;"

echo "==> Step 4: Resuming traffic to new primary database..."
psql -h 127.0.0.1 -p 6432 -U admin pgbouncer -c "RESUME production_db;"
echo "Zero-downtime cutover completed successfully in < 3 seconds!"

5. Sequence Synchronization & Reverse Replication

Logical replication replicates table row data but does NOT automatically increment PostgreSQL sequence objects (e.g. bigserial / GENERATED ALWAYS AS IDENTITY).

Immediately upon promoting the destination database, run an automated script to advance all sequences to MAX(id) + 1000 to prevent duplicate key collisions on subsequent writes.

SA
Sikander AliFounder & CEO • CKA

Founder & CEO | Principal Cloud Architect, TechnoFreaks

Fixed-Scope Migration EngineeringBacked by our 0% Data Loss Guarantee • Multi-AZ Replication

Planning a High-Stakes Cloud Migration or Database Engine Cutover?

Migrating monolithic databases and live customer workloads between AWS accounts, regions, or cloud providers carries zero margin for error. Our team designs multi-AZ replication cutover runs, dry-run rehearsed rollback gates, and dual-write synchronizations to guarantee 0% data loss and uninterrupted user sessions.

Implement This in Production

Ready to Upgrade Your Cloud Infrastructure?

Book a 30-minute technical architecture review with our senior DevOps leads to assess your migration roadmap and infrastructure optimization.