Migrating from MongoDB Atlas to a self-hosted replica set bought us control and cut our bill.
It also quietly removed something we had stopped thinking about: Atlas had been taking continuous backups for us the entire time.
After the migration, production data for Prochesta lived in on a single VPS.
No snapshots.
No off-box copy.
A , a bad migration script, or a dead disk would have been the end of it.
We had written "backups" as a follow-up task in the migration spec, which is the engineering equivalent of a sticky note on a bank vault.
The requirement we actually cared about was narrower than "back up the database".
Most real-world data loss at our scale isn't hardware failure — it's a deploy that writes garbage, or someone running an update without a filter.
Recovering to last night doesn't help when the damage happened at 14:20 and you noticed at 14:50.
We needed to recover to an arbitrary moment, not to a nightly snapshot.
The constraint nobody mentions: Community has no We chose Percona Backup for MongoDB (PBM), and immediately hit the limitation that shapes every decision downstream.
PBM offers physical backups — fast file-level copies that restore in minutes and barely touch the running server.
They work by opening a backup cursor via the aggregation stage.
That stage exists in Percona Server for MongoDB and in MongoDB Enterprise.
It does not exist in MongoDB Community, which is what the official image ships.
So on Community, PBM gives you logical backups only: every document read out through , compressed, and shipped off-box.
Two consequences, both accepted deliberately rather than discovered later: Backups cost CPU on the primary — and with a single-member replica set there's no secondary to offload the read to.
Restores insert documents and rebuild indexes, so restore time grows with data size much faster than backup time does.
At our current size that's minutes, not hours.
It's also the thing that will eventually justify swapping the image for Percona Server.
Knowing which constraint will force the next migration is worth more than pretending there isn't one.
We had already built one of these.
We didn't use it.
I maintain mongopit, a self-hosted MongoDB point-in-time recovery tool.
Its architecture is the same shape as what we ended up with: for consistent full snapshots, a daemon tailing the oplog and streaming compressed BSON segments to cloud storage, and to stop the replay just before the operation that ruined your day.
It ships GFS retention, a restore preview, and runs as a single Docker Compose service.
It stores backups on Google Drive via rclone.
That's the reason we reached for PBM instead — we wanted Cloudflare R2 as a first-class target, and the reasons are worth spelling out because they aren't really about Drive.
The data was already there.
Prochesta serves user uploads, receipts and generated PDFs from R2 through the S3 SDK.
Same account, same credentials, same billing, same dashboard we already check during an incident.
Adding Google Drive meant a second provider, a second service account, and a second thing to be wrong at 3am.
R2 has zero egress fees, and that's a reliability feature, not a line item.
Restore rehearsals pull the entire backup down.
If every drill has a bandwidth cost attached, drills quietly stop happening — and an unrehearsed backup is a rumour.
We wanted the cost of practising to be zero so there's never a reason to skip it.
Object storage semantics beat file-sync semantics for this job.
S3-style multipart uploads, predictable API rate limits, and a flat key namespace are what an unattended nightly job wants.
Drive service accounts bring their own operational texture: daily transfer ceilings, the shared-drive requirement for service accounts, and API quota errors that surface as a failed backup rather than a slow one.
PBM speaks S3 natively, so R2 needs configuration, not a translation layer. rclone's S3 backend could in principle point mongopit at R2 — but that's a path the tool