SD System Design In Depth
0%
design system-designinterviewfeedmedia

Design: a photo-sharing interview

Module 6.3: High-Scale Media & Operations Designs Updated Sep 18, 2026
📄 Cheat Sheet
System Design Mock Interview: Design Instagram
Aced (formerly Exponent) System Design Mock Interview: Design Instagram Watch on YouTube ↗

An actual mock interview of the photo-sharing problem. It covers upload, storage split, feed generation and the celebrity problem, and shows how to pace the answer.

0:00 / --:--

Designing a Photo-Sharing App (Instagram)

A photo-sharing service has two connected jobs: publish an author's image and help other people discover it. The image bytes, post metadata, and follower feed have different storage and delivery needs.

The classic Instagram system design interview covers uploading photos, storing relationships (following/followers), and generating the News Feed.

Storage Segregation

We separate our storage layers based on the type of data:

  • Binary Data (Photos/Videos): Stored in Object Storage (Amazon S3). Served globally via a Content Delivery Network (CDN) to reduce latency for end-users.
  • User Metadata and Social Graph: Relationships like "User A follows User B" need indexes for the actual access paths, such as followers and followees. Instagram historically used sharded PostgreSQL; graph or wide-column stores are alternatives only when their query, consistency, and operational tradeoffs fit the workload.
  • Post Metadata: Pointers to the S3 objects, timestamps, captions, and view counts are stored in a relational database or NoSQL store.

Generating the News Feed

When you open the app, you see a chronological (or algorithmically sorted) list of photos from people you follow. Generating this on the fly (pull model) by querying the database for "all photos by people I follow, sorted by time" is incredibly slow if you follow 1,000 people.

Instead, we use a Push Model (Fan-out on Write):

flowchart LR Author["Author Posts Photo"] --> PostService["Post Service"] PostService --> DB[("Metadata DB")] PostService --> Fanout["Fanout Service"] Fanout --> RedisUser1["Follower 1 Feed Cache"] Fanout --> RedisUser2["Follower 2 Feed Cache"] Fanout --> RedisUserN["Follower N Feed Cache"]

For an ordinary author, the Fanout Service can asynchronously push the post ID into followers' bounded feed caches. Reading one page is then a bounded range read plus batched hydration—not an O(1) operation independent of page size—and cache misses still need a durable fallback.

The Celebrity Problem

The push model breaks down for celebrities with 200 million followers. Pushing one photo to 200 million Redis lists takes too long and wastes huge amounts of memory, especially for inactive users.

A hybrid model can fan out ordinary authors and merge selected high-fanout authors at read time. The threshold is workload-dependent and moves cost rather than balancing it perfectly: reads now need bounded multi-source merge, ranking, pagination stability, and a policy for stale or missing cached entries.

Worked example: back-of-the-envelope numbers

  • Uploads: 100 million photos per day is about 1,160 per second on average, perhaps 3,500 at peak.
  • Storage: a 2 MB original plus 4 variants (1080, 640, 320 and a thumbnail) of about 600 KB in total, so about 2.6 MB per photo and about 260 TB per day. That is about 95 PB per year before replication or erasure coding. Old originals can move to colder storage tiers.
  • Reads: 500 million daily users each viewing about 100 images is 50 billion image fetches per day, about 580,000 per second. The CDN must absorb 95% or more of this.
  • Metadata: about 1 KB per post, or 100 GB per day, easily handled by sharded Postgres. Instagram sharded Postgres into thousands of logical shards mapped onto fewer physical servers.

Storage by data type

DataStoreAccess patternWhy
Image bytesObject storage + CDNWrite once, read many, cache forever (immutable URLs)Cheap, durable, cacheable at the edge
Posts, usersSharded Postgres (by user_id)Point lookups, recent posts by authorTransactions, secondary indexes
Follow graphSharded SQL or a graph store (Meta TAO)Followers of X, followees of YTwo indexes, one per direction
Feed timelinesRedis or Cassandra lists of post IDsRange read of the top NPrecomputed for fast opens
Likes and commentsWide-column store + countersWrite-heavy, counts shown on every postAvoids hot rows

Upload flow

sequenceDiagram participant App as Mobile app participant API as Post API participant S3 as Object store participant Q as Media queue participant W as Resize workers App->>API: request upload URL API-->>App: pre-signed PUT URL and media_id App->>S3: PUT original image App->>API: create post with media_id and caption API->>Q: media uploaded event Q->>W: generate variants and strip EXIF location W->>API: variants ready, post visible API->>Q: fan-out to follower feeds

Instagram's early engineering blog described generating IDs inside Postgres: 41 bits of milliseconds since a custom epoch, 13 bits of logical shard ID and 10 bits of per-shard sequence. The IDs sort by time, and the owning shard can be read directly from the ID. This is a useful detail to mention in an interview when explaining how IDs are generated.

Check yourself Question 1 of 3
Pre-signed uploads recall
From: Designing a Photo-Sharing App (Instagram)

In the recommended upload design, how do photo bytes get from the phone into object storage?

Key Takeaways

Store heavy media in Object Storage and serve it strictly through a CDN.
Use a Fan-out on Write (Push) model to pre-compute feeds for fast reads.
Adopt a Hybrid Fan-out model to prevent the 'celebrity problem' (thundering herd on write).
Scale relational databases by sharding users across multiple database instances.
Use asynchronous workers to process image resizing, thumbnail generation, and feed fan-out.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue