Sign in
TodayMapLearnPracticeReview
Library
15 MINinterviewCase StudiesNot started

Design Instagram

Design a photo-sharing social network with 500M DAU. Covers presigned-S3 photo upload with async transcoding, the feed-generation decision between fan-out-on-write vs fan-out-on-read (and the hybrid used for celebrities), Redis sorted-set feed caches, and CDN-fronted media delivery. The deep dive walks through why pure fan-out-on-write collapses for Justin Bieber and how the hybrid model solves it.

Why this matters

Instagram is the canonical 'social feed' design problem because it combines three of the hardest patterns in distributed systems: media storage at petabyte scale, a read-heavy personalized feed, and the fan-out-vs-pull trade-off that defines every social product. The hybrid fan-out model — push for normal users, pull for celebrities — is the answer to one of the most common system-design interview questions and is literally how Instagram, Twitter, and Facebook work.

Prerequisites
  • Content Delivery Networks
  • Object Storage
  • Cache Aside (Lazy Loading)
  • SQL vs NoSQL
Related
  • Design Twitter
  • Design News Feed
Used in

Foundational.

Lesson

How it works

What are we designing? An Instagram-style photo-sharing social network. Users post photos (with captions), follow other users, and browse a personalized feed of the most recent photos from people they follow. The system must handle massive read traffic (feed browsing dominates), large media uploads (photos and short videos), and real-time interactions (likes, comments).

The defining design challenge is the feed: when user A follows 500 people and opens the app, the system must assemble a personalized timeline of the latest ~1000 posts from those 500 people, sorted by recency, in under 200 ms — and it must do this for 500M daily users simultaneously. Feed generation strategy is the heart of this case study.

Functional requirements.

  • Users can post a photo with a caption and optional location.
  • Users can follow / unfollow other users.
  • Users can browse their feed (recent posts from followed users, paginated).
  • Users can like / comment on a post.
  • Users can view a profile with their own posts.

Non-functional requirements.

  • Feed latency: p99 < 200 ms (users expect instant app open).
  • Photo upload: < 5 s end-to-end including processing.
  • Availability: 99.99% (Instagram is daily-use; outages are news).
  • Storage: photos are forever; we never delete user content.
  • Scale: 500M DAU, 100M new posts/day, 10B feed reads/day.

Non-goals. No stories (24-hour content), no DM, no algorithmic ranking (v1 is chronological; ranking is layered on in design-news-feed).

Capacity estimation.

DAU & QPS. 500M DAU, average 20 feed refreshes/day = 10B feed reads/day = ~115K feed reads/sec average, peak ~500K/sec.

Posts. 100M posts/day, each ~500KB after compression (photo + thumbnail). 100M x 500KB = 50 TB/day of new media = ~18 PB/year. Stored on object storage (S3).

Feed metadata. Each post is ~1KB metadata (post_id, user_id, timestamp, caption hash, media_url). 100M x 1KB = 100GB/day of metadata = ~36 TB/year. Stored in sharded SQL.

Bandwidth. Photo upload: 100M x 500KB / 86400 = ~580 MB/s = 4.6 Gbps ingress. Feed read: each feed refresh returns ~20 posts x 100KB (compressed thumbnails) = 2MB; 500K/sec x 2MB = 1 GB/s = 8 Gbps egress from origin. With CDN serving photos, origin egress drops to ~10% (0.8 Gbps).

Feed cache. 500M users x their pre-computed feed (last 1000 post_ids) x 8 bytes = 4 GB. Trivially fits in Redis. But if we fan out writes, each new post by a user with 100 followers pushes the post_id into 100 feeds; with 100M posts/day x avg 200 followers = 20B fan-out writes/day = 230K writes/sec. Manageable.

APIs.

code
POST /v1/posts              (multipart: photo + caption + location)
GET  /v1/feed?cursor=...    -> { posts: [...], next_cursor }
GET  /v1/users/:id          -> profile + recent posts
POST /v1/users/:id/follow
DELETE /v1/users/:id/follow
POST /v1/posts/:id/like
POST /v1/posts/:id/comments

Photo upload uses presigned S3 URLs (valet keys) so the client uploads directly to object storage — the API server never proxies the photo bytes. After upload, the client calls POST /v1/posts with the S3 object key; the server kicks off an async transcoding pipeline and inserts the post metadata. The post appears in feeds only after transcoding completes (a few seconds).

Data model.

Users (sharded SQL):

code
users (id BIGINT PK, username, bio, avatar_url, created_at)

Posts (sharded SQL, sharded by user_id):

code
posts (id BIGINT PK, user_id, caption, media_url, location GEO,
       created_at, like_count, comment_count)
INDEX (user_id, created_at DESC)        -- for profile pages

Follows (sharded SQL or graph DB):

code
follows (follower_id, followee_id, created_at, PRIMARY KEY (follower_id, followee_id))
INDEX (followee_id)                     -- for 'who follows me'
INDEX (follower_id, created_at)         -- for 'who do I follow'

Feed cache (Redis sorted set per user):

code
key: feed:{user_id}
value: ZSET member=post_id, score=created_at   -- capped at top 1000

Photo storage (S3):

code
bucket: ig-media-prod
key:    {user_id}/{post_id}/{variant}    -- variant = orig|1080|720|480|thumb

Multiple resolutions are generated by the transcoding pipeline for adaptive delivery.

Deep dive: feed generation — fan-out on write vs read.

Fan-out on write (push model). When user P posts, the system looks up P's followers and pushes post_id into each follower's Redis feed (ZADD with score = post timestamp). Reads are O(1): just ZRANGE the user's feed.

Pros: feed reads are instant (single Redis call). Cons: write amplification. A user with 1M followers triggers 1M Redis writes per post. For Justin Bieber (100M followers), a single post = 100M Redis writes — that crushes the cluster.

Fan-out on read (pull model). When user U opens their feed, the system looks up the users U follows (say 500), fetches their recent posts from each user's post_id list (already indexed by (user_id, created_at)), merges by timestamp, returns the top 20.

Pros: no write amplification — posting is O(1). Cons: feed reads are expensive — 500 DB queries per refresh, even after caching.

Hybrid (our choice). Fan-out on write for normal users (most users have <500 followers); fan-out on read for celebrities (>10K followers). This caps the worst-case fan-out at 10K writes per post while keeping feed reads fast for 99% of users.

Concrete numbers. Average user has ~200 followers. 100M posts/day x 200 = 20B fan-out writes/day = 230K writes/sec. Top 0.1% of users (celebrities) average 10M followers — if we fanned them out, 1 post = 10M writes. By pulling them on read, we save ~10M writes per celebrity post and pay only ~500 extra DB queries per feed read for users who follow a celebrity (most users follow 1-5 celebrities).

Capping the feed. Each user's feed cache holds the last 1000 post_ids (ZSET, score = timestamp, ZREMRANGEBYRANK to evict beyond 1000). If the user scrolls past 1000, we fall through to the pull model for older posts.

Stale feed problem. With fan-out on write, if a user is unfollowed AFTER their post was pushed, the post lingers in the unfollower's feed. Mitigation: on unfollow, ZREM all of unfollowed-user's recent post_ids from the unfollower's feed. Cheap because the unfollower typically only has a few hundred of the unfollowed user's posts in their feed.

Deletion propagation. When a user deletes a post, we must remove it from every follower's feed. Mitigation: lazy deletion — mark the post deleted; feed reads filter deleted post_ids at hydration time. Periodic background job ZREMs stale deleted post_ids.

Bottlenecks and failure modes.

  • Celebrity post spike. A celebrity with 100M followers posts; even at pull-on-read, the next minute's feed reads all hit the celebrity's post shard. Mitigation: cache the celebrity's recent posts in a separate hot-key cache; replicate to multiple shards.

  • Redis feed cache failure. If Redis dies, every feed read falls through to the pull model — 500 DB queries per read at 500K reads/sec = 250M queries/sec. The DB dies. Mitigation: Redis cluster with replicas + circuit breaker that returns stale feeds with a 503-and-retry after 1s, rather than crushing the DB.

  • Transcoding pipeline backlog. If transcode workers fall behind (viral video, instance failure), posts take minutes to appear in feeds. Mitigation: autoscale workers based on queue depth; show a placeholder thumbnail immediately so the post appears in feeds, then swap to full-res when transcode completes.

  • S3 upload failures. If a presigned URL expires before the client finishes uploading a large video, the upload fails. Mitigation: use multipart upload with S3 (each part gets its own presigned URL; parts can retry independently).

  • Hot post. A post goes viral — 1M likes in 5 minutes. The like counter row becomes a write hotspot. Mitigation: shard the counter across N rows (e.g. likes_0, likes_1, ... likes_9) and sum on read; or use a CRDT counter (Redis INCR is single-threaded — needs sharding for >10K writes/sec).

  • Fan-out write storm. When a celebrity crosses 10K followers, our policy flips from push to pull. The transition must not retroactively remove their pushed posts from existing feeds. Mitigation: just stop pushing NEW posts; old pushed posts age out naturally.

Scaling strategy and trade-offs.

Database sharding. Shard posts and follows by user_id (so a user's posts and follow graph live on one shard). Cross-shard feed reads are reduced by the fan-out-on-write cache.

CDN for media. Photos are served from a CDN (Cloudflare, CloudFront). Cache hit rate >95% for popular posts; origin egress drops 10x. Cache key includes the variant resolution so the CDN serves the right size.

Multi-region. Deploy feed-service + Redis feed cache in 5+ regions. Posts DB is globally replicated (eventually consistent); writes go to a single primary region. Feed reads hit the local Redis cache.

Transcoding autoscaling. Scale transcode workers based on Kafka queue depth. Each worker pulls a job, transcodes (FFmpeg / libvips), uploads variants to S3, marks the post ready.

Trade-offs made explicit.

  • We chose fan-out on write for normal users — gained instant feed reads, lost cheap celebrity posting (mitigated by hybrid).
  • We chose chronological feed — gained predictability and simplicity, lost engagement optimization (algorithmic ranking adds 5-20% time-on-site but is a whole ML system of its own).
  • We chose presigned S3 uploads — gained no photo bytes through API servers, lost the ability to do server-side validation before storage (mitigated by post-upload content moderation).
  • We chose lazy deletion of feed entries — gained cheap delete, lost immediate consistency (a deleted post may appear briefly in feeds before hydration filters it).
Check yourself
interview

Justin Bieber (100M followers) posts a photo. With pure fan-out-on-write, what happens?

Pick one answer.

Check yourself
interview

Your feed cache is Redis. Redis dies. What happens?

Pick one answer.

Engineering mental model

Mental model. Think of Design Instagram as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Design Instagram mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”

Design lens

Before choosing Design Instagram, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.

Original NO CAP systems visual for Design Instagram.
Image unavailable. Original NO CAP systems visual for Design Instagram.
Design Instagram: a compact system-thinking visual.— Original NO CAP visual.
// Pseudocode
request = receive()
result = design_instagram(request)
return result

// Production questions:
// 1. What happens on timeout?
// 2. Can this operation be retried safely?
// 3. What is the bottleneck?
A minimal engineering sketch for reasoning about Design Instagram.

Back-of-the-envelope reasoning

Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.

Interactive sandboxdeterministic

Interactive thought experiment: Design Instagram

Change the variables below and predict what breaks first in Design Instagram. The production lab can later reuse these same inputs.

System pressure6%
Try this

Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.

Hint

If you are stuck on Design Instagram, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.

Check yourself
solid

You increase traffic by 10× in a system using Design Instagram. What should you inspect first?

Pick one answer.

Check yourself
interview

Which statement is the safest engineering habit when using Design Instagram?

Pick one answer.

Try this
interview

You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.

Production scenario: your system uses Design Instagram, traffic suddenly spikes, and p99 latency doubles. What is your first move?

Interview drill

Answer this without notes: When would you choose Design Instagram, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.

Engineering lens

A useful engineering lens for Design Instagram: define the problem it solves, the simpler design that fails first, the constraint that forces you to introduce this concept, and the new failure modes the concept creates.

Numerical sanity check

Back-of-the-envelope reasoning beats fake precision. State your traffic, payload, concurrency and growth assumptions explicitly, then calculate enough to know whether the current architecture is orders of magnitude away from the target.

Check yourself
interview

Do not optimize for a memorized definition. Reason from the workload and failure mode.

Imagine the simplest version of a system using Design Instagram. What breaks first as traffic grows by 10×, and what would you change before reaching 100×?

Pick one answer.

Trade-offs

What you gain, what you pay

Pros
  • +Feed reads are O(1) for normal users thanks to fan-out-on-write into Redis ZSETs.
  • +Presigned S3 uploads keep photo bytes off the API servers.
  • +CDN absorbs >95% of media egress; origin stays small.
  • +Hybrid fan-out (push normal, pull celebrity) bounds worst-case write amplification.
Cons
  • −Celebrity posts still stress the pull path — needs a hot-post cache.
  • −Redis feed cache failure cascades into DB collapse without a circuit breaker.
  • −Fan-out-on-write makes deletion and unfollow propagation expensive.
  • −Chronological feed is less engaging than algorithmic (intentional v1 scope).
Failure modes

How this breaks in production

  • Celebrity post spike saturates a celebrity's post shard — needs hot-post cache replication.
  • Redis feed cache death -> DB collapse — needs circuit breaker + replicas.
  • Transcoding pipeline backlog delays posts appearing in feeds — needs autoscaling.
  • Like-counter hotspot on viral posts — needs counter sharding.
  • S3 presigned URL expiry breaks large video uploads — needs multipart upload.
Common mistakes

Don't fall into these traps

  • •Pure fan-out-on-write for all users — crushed by celebrity posts.
  • •Pure fan-out-on-read for all users — crushes DB with 500-query joins.
  • •Using Redis as the source of truth for posts — loses data on Redis failure.
  • •Proxying photo uploads through the API server — wastes bandwidth and CPU.
  • •Forgetting to handle unfollow / delete propagation in pushed feeds.
Where you see it

Real systems using this

Instagram (Facebook/Meta)Twitter / X timelineThreadsPixelfed (fedora equivalent)
Teardowns

How real systems implement this

  • Instagram — Fan-out-on-write for normal users into Redis ZSETs, pull-on-read for celebrities. Photo upload via presigned S3 URLs. CDN-served media. Documented in various Meta engineering blog posts.
  • Twitter — Similar hybrid fan-out, with celebrity detection at ~10K followers. Uses Redis Gemini clusters for timeline caches.
  • Facebook — Uses a more sophisticated pull-on-read with EdgeRank ranking, but the storage primitives are the same.
Interview prompts

Practice saying it out loud

  • Q1Design Instagram. How do you generate the feed?
  • Q2Justin Bieber posts a photo. What happens to your system?
  • Q3How do you handle photo upload at scale without overloading API servers?
  • Q4Your Redis feed cache dies. What happens next?
Research

Further reading & references

System Design Primer
Open source
ByteByteGo — Scale from zero to millions
ByteByteGo
System Design Tutorial
GeeksforGeeks
System Design Roadmap
roadmap.sh
Case Studies reference
Reference
Case Studies reference
Reference
Case Studies reference
Reference

Core explanations are original NO CAP material. External references are provided for deeper study and standards.

What next?

Mark as understood once the mental model clicks.

Next recommended

Design Twitter