The constraint: a library bigger than its host
Every card needs its image, in three sizes: full artwork for detail views, thumbnails for lists and hover previews, and a mid-size preview for everything in between.
Multiply that by a full card catalog and you get a library of roughly 150 GB — one that lived on a single disk beside the application, growing with every set release, and serving every byte through the app host itself.
That arrangement had an expiry date. The application server is for logic, not bulk byte-serving, and the test environment — a small virtual machine — could only ever hold a lean subset of key sets. The library needed a home built for static assets: object storage, with the app generating deterministic URLs instead of shuffling files.
The design: derivations and immutable URLs
Three derivations, each with a job: full-resolution artwork, small thumbnails, and mid-size previews. Every object immutable, addressed by a deterministic URL derived from the card — same card, same URL, forever — with long cache lifetimes. Immutability does quiet heavy lifting here: nothing is ever overwritten, so caches are always right, rollbacks are trivial, and the app can build image URLs with zero storage API calls on the serving path.
The migration: a quarter-million objects, then proof
The bulk move itself was the easy part. The verification pass was the real work: object counts reconciled, total bytes compared, key sets diffed, checksums sampled — including a 200-for-200 identical spot check — and anonymous reads confirmed from the outside, the way a visitor's browser would see them. A follow-up backfill of over a hundred thousand preview derivations got the same treatment. The migration wasn't done when the upload finished; it was done when the evidence said so.
The result: storage serves bytes, the app serves logic
Production imagery now comes straight from object storage while the app does what it's good at. The test environment carries its lean subset without apology — it was never meant to hold the full tree. And because every URL is deterministic and every object immutable, the image pipeline has no moving parts left to break.
The lesson
Bulk moves earn trust through verification, not hope. Anyone can copy files; the work that matters is the proof pass afterward — and designing the addressing scheme so there's nothing left to verify next time.