In September we decommissioned Fly.io — the last hosted service left in the platform, and the one the package registry had been running on.
The migration itself was orderly. The registry was declared in our own control plane, deployed to one of our app hosts in phases, and its blob storage moved onto object storage on our own fleet, versioned, with a second provider retained for off-host backups.
The thing we did not migrate
A day later someone wrote down the sentence that made the whole migration worth re-examining:
The Fly volume snapshots that were the implicit backup went away with the move to the new host and nothing replaced them.
Nobody had chosen to stop taking backups. The backups were never a decision. They were a property of the platform we were running on — the hosted provider snapshotted the volume, so a database dump felt redundant. Removing the provider removed the property, and no checklist item said "confirm that the thing you were getting for free is now something you do."
This is the general shape of platform migrations, and it is worth naming: you migrate the things you configured, and you lose the things you were merely given.
The replacement
A backup script now runs a database dump from inside the database container, compresses it, and streams it to the backup bucket on our own storage, keeping thirty. It is deliberately paranoid in one specific way: it refuses to upload an archive smaller than 1 KB.
That threshold is the kind of check that only makes sense after you have been burned. A dump that fails silently produces a file — an empty one, or one containing an error message. The backup job succeeds, the bucket has an object in it, the monitoring is green, and the restore fails at the moment you need it. The size check is how a failed dump becomes a failing backup.
What we changed
Backups are now a named, scheduled job with an assertion about the artifact, not an assumption about the platform. And the migration checklist has a new section that is only one question, asked of every dependency: what does this provider give us that we did not ask for, and where does it go?
For Fly it was volume snapshots. There are other answers waiting in other providers — an implicit TLS certificate, a log retention window, a rate limit that quietly protects a database. The point is not that the answer is always somewhere in your configuration. The point is that it is usually not.
