Migrating ~80 GiB of Customer Media to CDN-Backed Object Storage
Stack
- DigitalOcean Spaces
- CDN
- Node.js
On this page
Key results
- Media migrated
- ~80 GiB
- Storage from app servers
- Decoupled
- Media delivery
- CDN
- Moving a file
- DB update
Problem
Better Coaching is a multi-tenant SaaS platform that football clubs and academies use to manage coaching, players and training content.
The Media Library was wired directly to the server filesystem. A folder in the app was a real directory on the application server. Creating a folder created a directory, renaming it renamed the directory, and moving a file moved it on disk. The filesystem was acting as both storage and part of the application’s data model.
By the time the platform held about 80 GiB of customer images and videos, that design was causing problems:
- media storage was tied to the application server, which complicated infrastructure changes and future horizontal scaling;
- folder operations depended on physical filesystem operations;
- every media request came from the same server, and customers reported slow-loading images and videos, especially farther away from it.
Better Coaching needed a media architecture that could grow without tying files to the app servers, serve users in different regions well, keep the folder-based Media Library customers already used, and preserve all existing media through the transition.
My role
I was the team lead on this migration. I set the direction for moving media to object storage behind a CDN and for separating the Media Library’s structure from physical storage, and led the team through the data-model redesign and the migration of existing media.
I also made sure the user-facing Media Library stayed stable throughout, so an infrastructure change didn’t become a workflow change for customers.
Technical approach
Moving media to S3-compatible object storage
We chose DigitalOcean Spaces, an S3-compatible object storage service, as the new storage layer. Uploaded media now lives in object storage instead of on the application server, so storage capacity grows independently of the compute running the app.
Delivery moved behind a CDN, so cached files are served closer to users instead of every request going back to the application server.
Virtual folders
Files and folders became separate concepts. The image or video is stored as an object in Spaces, and the database stores what’s needed to organize and display it:
- the object key or media reference;
- the original filename;
- the media type;
- its folder;
- relevant file metadata.
Folders are database entities that reference their parent folder, and each media item references the folder it belongs to. The hierarchy looks the same to users but no longer depends on how files are physically stored.
Migrating ~80 GiB with its structure
We transferred the existing images and videos to the new storage first and only later switched the production application over, so the data migration and the application switch could be handled as separate problems.
The hard part wasn’t copying bytes. It was making sure the media still meant the same thing afterwards:
- nested directories rebuilt as folder relationships;
- old file references resolving correctly;
- duplicate files handled carefully;
- no customer left with missing or misplaced media.
Those details would have been easy to miss if we had treated this as a simple storage-provider switch.
Keeping the Media Library unchanged for users
Although the architecture underneath changed substantially, the Media Library experience stayed the same during the migration. Users navigated the same folders and found the same images and videos.
Changing infrastructure, data organization and customer workflows at once would have multiplied the risk. With the new model in place, the Media Library can now be improved separately.
Decisions & trade-offs
A file migration became a data-model redesign
At first the plan looked simple: move the files, update their URLs, switch over. But in the existing Media Library, folders weren’t just visual organization. They were the filesystem. Nested folders were nested directories, media references depended on those paths, and renaming a folder changed the path of everything below it. With ~80 GiB of customer media, we couldn’t copy the files and ignore those relationships.
We weren’t only migrating storage. We had to separate the Media Library’s logical structure from its physical storage.
Virtual instead of physical folders
Making folders virtual turned common operations from file operations into data changes. Before:
- Move file
- Move physical file on server
- Update filesystem path
After:
- Move file
- Update folder relationship in database
The object itself never moves, and renaming or reorganizing folders is mainly a database operation. It also gives a cleaner base for richer metadata, search and other ways of organizing media.
Results
Media off the app server
About 80 GiB of existing customer images and videos moved off local server storage, so persistent media is no longer tied to the server running the application.
Independent delivery
Media is served through CDN-backed infrastructure instead of depending on the application server’s location and capacity, which directly addressed customer complaints about slow media loading.
Easier scaling
The application servers no longer hold persistent media, so infrastructure and storage can evolve independently, and one of the main obstacles to running the application on multiple instances is gone.
Much simpler file organization
Moving or renaming files and folders no longer requires matching filesystem operations. The Media Library is now an application feature built on database relationships, not a UI that mirrors the server’s disk.