Better Coaching

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:

  1. Move file
  2. Move physical file on server
  3. Update filesystem path

After:

  1. Move file
  2. 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.