Mongo → Postgres — while the lights stayed on.
A zero-downtime migration from MongoDB to PostgreSQL for a healthcare platform with continuous real-world traffic. Two-way sync, custom indexes, redesigned API. Twelve months from kickoff to shutdown.
The architecture, during the two-database phase
The five phases, by traffic split
Read-write Mongo only
Months 0 — 1 · pre-migration baselineCapture latency, throughput, and query-shape baselines. Write the migration playbook before writing any migration code.
Postgres shadow
Months 2 — 4 · dual-writeEvery write hits both databases. Reads still come from Mongo. Sync service polices any drift.
Postgres read trickle
Months 5 — 7 · staged read cutover5% to 75% of reads served from Postgres, by endpoint risk class. Rollback stays a feature flag away.
Postgres primary
Months 8 — 10 · write cutoverWrites flip to Postgres-first. Mongo becomes the warm shadow. Nobody fell back.
Mongo shutdown
Months 11 — 12 · sunsetCluster decommissioned. Sync service archived. Infra cost drops from baseline. Playbook closed.
The problem
A healthcare platform on Mongo since 2017 had outgrown the model.
The data was relational in spirit and document in storage; every new feature required a compensating index; the bill was climbing.
The approach
The plan was the deliverable. Before any sync service, before any schema, I wrote a playbook covering the five traffic phases, rollback paths, success criteria, and alarms.
The technical heart was a small sync service reading Mongo's change stream and writing into Postgres, plus a reverse path doing the same thing the other way.
Migrations are 20% data, 80% organisation. The schema was the easy half.From the post-migration retro
The hard bits
- —Schema embedding vs joins. Mongo embedded patient to encounters to observations. Postgres normalized them. The API kept the embedded read path alive with a read-side view.
- —Indexes for compatibility, then cost. Phase-one indexes mirrored Mongo query shapes; phase-two indexes were rewritten for the Postgres planner.
- —The reverse sync. Postgres to Mongo required a versioning scheme that did not exist in the original schema.
- —Rollback belief. The rollback plan had to be real enough that the team trusted every phase.
What I'd do differently
I would write the reconciliation job in week one. We wrote it in month four after spending three months arguing about whether we needed it.
I would buy managed Postgres earlier. The infra savings came partly from leaving Mongo, but also from picking a better Postgres host.
Read another case study
Edulinks.ng — a community on the company stack
Veraclins's Q&A and learning community for Nigerian students, built on the company's shared design system and tooling.