The move has two halves. The data moves from SQLite to Postgres, then the system moves from one virtual machine to containers. The data goes first, because it is the half that can lose something.
What is done
- The decisions and the baselines. The platform choice is made, and the numbers every later gate compares against are recorded.
- A container image that builds and runs. It is not in production yet.
- Postgres itself, written as infrastructure as code, with a pilot store proven on the sandbox first.
- One connection helper that every store opens through, so a store can move between SQLite and Postgres without its own code changing.
- Tools to compare the two databases and to resync them, and a check in CI that fails any new code that opens a database some other way.
- The first thirteen stores writing to Postgres in production, next to SQLite, compared every night, with more following.
What the shadow writes are for
Nothing reads from Postgres yet. Every write still lands in SQLite, and the copy in Postgres is checked against it every night. A mismatch is a bug I find before anyone depends on the new database.
When a full week of jobs matches, reads move. When reads have run clean, SQLite stops. Nothing is deleted until that has run clean too.
Moving in waves
The stores move in order of risk. Ledgers with a single owner went first. User data and derived data are following now. The core stores go last, once everything around them has a clean record. Each wave goes to the sandbox, then into the production shadow, then waits for its compares to come back clean.
What is next
The rest of the stores, then the go-live window and at least a week of soak on Postgres. After that comes the container half: environments as code, a pipeline with security gates, monitoring, and a staging environment that runs on dry run for a week. Then the cutovers, in waves: scheduled jobs first, then the receiver and the worker, then the web apps. The old machine stays on for two weeks after the last cutover, just in case.
What I'd tell someone starting the same move
- Write the plan as tick boxes, and only tick one when there is a commit or a log line to point at.
- Put one helper in front of every database before any data moves. After that, moving a store is a setting, not a rewrite.
- Make CI enforce the helper, so nothing new slips round it.
- Shadow first, switch later. The week of clean compares is the evidence, not the plan.
I'll write the next one when reads move.
