System designCore10 min

Object & blob storage

Photos, videos, documents and backups are too big for a database and too important for a server's disk. Object storage keeps them as whole files under keys, in a service built to hold practically unlimited data durably. It is simple to use, and its few rules decide how uploads, serving and costs should be designed.

1 · The problem

Files belong neither on app servers nor in the database

A marketplace for secondhand furniture gets 30,000 new listings a day, each with about 8 photos of 4 MB. That is 30,000 × 8 × 4 MB = 960 GB of photos a day, about 350 TB a year. Where should they go?

Put the photos…

What goes wrong

On the app server's disk

Another server can't see the file, and a replaced server takes its files with it (Stateless services & sessions)

In the database

Nearly a terabyte a day swells backups and replicas, and streaming bytes ties up the database's memory and connections

In a shared file system

Built for files that many machines open and edit in place, which photos never need

In object storage

Built for exactly this: whole files, any number, read from anywhere over HTTP

Storage comes in three shapes. Block storage is a raw disk divided into fixed-size blocks; databases and virtual machines run on it. File storage is a shared file system with folders and permissions. Object storage keeps each file as a single object under a unique name, reached through a web API. Azure calls objects blobs; the idea is the same.

2 · The model

An object store is a huge key-value store for whole files

Objects live in buckets. Each object has a key (its name), a value (the bytes), and metadata such as its content type and size. You work with whole objects through HTTP: PUT to store, GET to read, DELETE to remove.

Storing and reading one photo

http
PUT /listings/8812/photo-1.jpg HTTP/1.1
Host: furniture-photos.storage.example
Content-Type: image/jpeg
Content-Length: 4194304

<4 MB of bytes>

GET /listings/8812/photo-1.jpg HTTP/1.1
Host: furniture-photos.storage.example

A few rules shape everything that follows:

Rule

What it means

Flat names

listings/8812/photo-1.jpg is one key; a "folder" is just a shared key prefix

Whole objects only

A change means uploading a complete replacement; no edits or appends in S3 or Google Cloud Storage (Azure has append blobs)

Strong reads after writes

In S3, a read that starts after a successful upload, overwrite or delete sees it, and listings include it

Last writer wins

Two uploads to one key don't lock or conflict; the later one replaces the other, and nothing spans several keys atomically

3 · Uploads

Let clients upload straight to storage with a short-lived signed URL

If every photo passed through the app servers, they would spend their time copying 960 GB a day of bytes they never look at. Instead, the app hands the browser a signed URL: a link that grants permission for one operation on one key until it expires, signed with the app's credentials. The bytes go straight to the object store:

The app server checks who may upload what and chooses the key; it never touches the bytes. The URL works for that one key, for 15 minutes.

A signed URL is a bearer token: anyone who has it can use it until it expires, so keep lifetimes short (S3 and Google Cloud Storage allow at most 7 days) and scope each one to a single key and method. Downloads of private files work the same way, with a signed GET.

Big files, like a seller's 2 GB video walkthrough, use a multipart upload: the file is split into parts that upload independently, in parallel and in any order, and a failed part is retried alone instead of restarting the whole file. In S3, a single PUT takes up to 5 GB, parts are 5 MiB to 5 GiB, up to 10,000 of them, and multipart is worth it from about 100 MB.

4 · Metadata

Keep the bytes in the object store and the facts in a database

The object store answers one question well: give me the object with this key. The marketplace needs many others: the photos of listing 8812 in order, every listing by a seller, photos still waiting for moderation. Those belong in a database row per photo, holding the key, size, checksum, order and status. Pages query the database, then build URLs from the keys.

Uploads also start work. Object stores can send an event when an object is created, which a worker uses to make thumbnails, strip location data from photos and run moderation:

Object storephoto-1.jpg arrivesThumbnail workerresize, strip GPSDatabasestatus: readycreated event, queuedwrite thumbnailsmark readyObject storephoto-1.jpg arrivesThumbnail workerresize, strip GPSDatabasestatus: readycreated event, queuedwrite thumbnailsmark ready

Processing happens after the upload, from a queue, so a slow resize never holds up the seller (Message queues & pub/sub).

Two loose ends need a rule each. Uploads that start but never finish leave orphan objects and half-done multipart uploads, which a cleanup rule deletes after a day or two. And deleting a listing must delete its objects too, or the bill keeps growing for files nobody can reach.

5 · Serving

Serve objects through a CDN, and name them so they never change

Reading straight from an object store is fine for a back-office tool, but small objects take roughly 100 to 200 ms to start arriving, and every view is another request to the store. Public photos go through a CDN, which keeps copies at edges near buyers (Content delivery networks (CDNs)).

Caching works best when an object's content never changes under its name. Put a version or a hash of the content in the key, photo-1.v2.jpg or 3f9a…c1.jpg, and a new photo gets a new key. The CDN and browsers can then cache each key for a year, and nothing ever needs to be purged.

Key

Cache it for

After the seller replaces the photo

photo-1.jpg

Minutes, or until purged

Old copies keep being served until they expire or are purged

photo-1.v2.jpg

A year

The listing points at a new key; old copies are never asked for again

Object stores scale request rates per key prefix. S3 handles at least 3,500 writes and 5,500 reads a second per prefix, with no limit on the number of prefixes, and it adds capacity gradually, answering 503 Slow Down while it does. Keys that spread across many prefixes, such as listings/8812/…, spread the load; one hot prefix behaves like any hot partition (Hot partitions & skew).

6 · Durability and cost

Durable is not the same as backed up, and storage only ever grows

S3 is designed for 99.999999999% durability, "eleven nines", by storing every object across at least three Availability Zones: separate data centers, kilometers apart. As a model, that is an annual loss chance of 10⁻¹¹ per object: across 10 million objects, 10,000,000 × 10⁻¹¹ = 0.0001 objects a year, one every 10,000 years.

That protects against hardware. It doesn't protect against your own code: a bug that overwrites every thumbnail, or a script that deletes the wrong prefix, is carried out durably. The defenses are separate features:

  • Versioning keeps every previous version of an object, so an overwrite or delete can be undone.
  • Object lock forbids deleting or overwriting for a set period, for records that must survive.
  • Replication to another region guards against losing a whole region (Disaster recovery & backups).

Cost is mostly storage that grows forever. Storage classes trade price per gigabyte against access:

Class (S3 names)

Access

Catch

Standard

Milliseconds

Highest storage price

Standard-Infrequent Access

Milliseconds

Retrieval fee; billed for at least 30 days and 128 KB

Glacier Instant Retrieval

Milliseconds

Higher retrieval fee; at least 90 days

Glacier Flexible Retrieval

Minutes to hours, after a restore

At least 90 days

Glacier Deep Archive

Hours, after a restore

At least 180 days

Lifecycle rules apply these automatically by age: move photos tagged as sold to Infrequent Access after 90 days, and delete them two years later. The deletion rule is the one that changes the curve:

  • Keep everything
  • Delete 24 months after the sale
05001,000010203040Months of operationPhotos stored (TB)

960 GB a day ≈ 29 TB a month. Kept forever, storage passes 1,400 TB in four years; with a 24-month rule it levels off at about 700 TB, assuming items sell soon after listing.

7 · In practice

A checklist for files in a system design

When a design involves user files, the same handful of decisions come up every time:

Decision

Usual answer

Where the bytes live

Object storage, never app servers or database rows

How uploads arrive

Directly from the client with a short-lived signed URL; multipart for big files

Where the facts live

A database row per object, holding the key and status

How processing starts

An upload event into a queue, consumed by workers

How files are served

Through a CDN, with keys that change when content changes

How mistakes are undone

Versioning, plus replication for whole-region loss

How cost is controlled

Lifecycle rules to cheaper classes, and deletion

Also:

  • Keep buckets private and grant access with signed URLs or the CDN, never by making a whole bucket public.
  • Check what you received: verify size and checksum, and the real content type, before marking an upload ready.
  • Don't build a file system on top: renames, appends and many small edits fight the whole-object model; use a database or file storage for those.

Check yourself

5 questions

1. Why do sellers upload photos directly to the object store with a signed URL, instead of through the app servers?
2. A seller's signed upload URL leaks in a public chat. What limits the damage?
3. A bug overwrites every thumbnail with a blank image. The store is designed for eleven nines of durability. Can you recover the originals?
4. The listing page needs a listing's photos in the seller's chosen order. Where should that order come from?
5. Why put a content hash or version in each photo's key?

Takeaways

Remember this

  • Object storage keeps whole files under keys in a flat namespace, read and written over HTTP; objects are replaced, never edited.
  • Clients upload directly with short-lived signed URLs, and large files go up in parallel parts.
  • The bytes live in the object store; the facts you query live in a database row per object.
  • Serve public objects through a CDN, with keys that change whenever the content does.
  • Eleven nines of durability doesn't undo your own mistakes; use versioning, replication and lifecycle rules.