Moving a live 120GB+ production PostgreSQL database from AWS EC2 to
Hostinger VPS with zero downtime โ navigating OID mismatches, WAL slot
exhaustion, partition table replica identity failures, and disk space
constraints.
RoleDatabase & DevOps Engineer
Database Size120GB+ (PostgreSQL Database)
MethodLogical Replication
TypeZero-Downtime DB Migration
The Problem
A production database that couldn't afford an hour of downtime โ let
alone a weekend
PostgreSQL (120GB+) was on AWS EC2 serving a live production
application. The client needed to migrate to another VPS to reduce
costs โ but had no maintenance window. pg_dump and restore would
require 4โ6 hours of downtime. Logical replication was the only viable
path.
Risk: Logical replication has strict requirements
around replica identity, partition tables, sequences, and OIDs โ any
one of which can silently cause replication to lag or fail without
obvious errors.
Migration Approach
Six phases โ the wrong order would be catastrophic
1
Schema migration first (pg_dump schema-only)Tables, indexes, constraints, and sequences created on target
before replication starts. Replication cannot create tables โ
only replicate rows.
2
Set replica identity on all tablesSeveral legacy tables lacked primary keys. Applied REPLICA
IDENTITY FULL to those โ with the trade-off of larger WAL
records for updates and deletes.
3
Create publication on source, subscription on targetCREATE PUBLICATION rsb_pub FOR ALL TABLES on source. CREATE
SUBSCRIPTION rsb_sub on target. PostgreSQL begins table sync
then streams live WAL changes.
4
Monitor replication lag until near-zeroMonitored pg_stat_replication.write_lag. Waited for lag under
100ms across all tables before scheduling cutover.
Post-migration nightly backup pipelineShell script: pg_dump at 02:00 IST โ gzip โ rsync to backup
server โ SMTP alert on success or failure.
Key Challenges
Four problems that nearly derailed the migration
๐ข
OID mismatches breaking FK constraints on targetpg_dump schema-only doesn't preserve OIDs. Custom type
references broke on target. Fixed with --no-oid flag and manual
review of all custom type definitions.
๐พ
WAL slot exhaustion filling EC2 diskA 4-hour network interruption caused the slot to hold 18GB of
WAL, nearly filling the disk. Fixed with max_slot_wal_keep_size
= 10GB and CloudWatch alarm.
๐
Partition tables not replicating โ silent failureLogical replication doesn't auto-replicate partition child
tables. Monthly audit_log partitions were silently excluded.
Fixed by explicitly listing all partition children in the
publication.
๐
pg_hba.conf blocking replication connectionpg_hba.conf was missing the replication entry for the target
IP. Subscription silently failed to connect. Fixed by adding:
host replication rsb_repl_user {target_ip}/32 md5.
Outcomes
120GB moved โ 47 seconds of customer-facing downtime
47s
Total application downtime
120GB+
Data migrated successfully
0
Rows lost during migration
60%
Infrastructure cost reduction
Result: 100% data integrity confirmed via row
counts, sequence values, and spot-check queries against both
databases post-cutover.