Photo Sharing App (Instagram)
Problem statement
Design a service where users upload photos, follow others, and browse a feed of photos with likes and comments — optimised for fast image delivery worldwide.
Requirements
Functional
Upload photos (with captions, tags); generate multiple sizes.
Follow users; home feed and profile grid.
Like, comment; notifications.
Explore/search by hashtag (basic).
Non-functional
Image load p99 < 200 ms globally (CDN).
Uploads durable; processing within seconds.
500M DAU; 100M uploads/day.
Read-heavy: 100:1.
Back-of-the-envelope estimates
- 100M uploads/day × 2 MB original + ~1 MB of derived sizes = 300 TB/day → ~100 PB/year. Object storage with lifecycle tiering.
- Feed reads: 500M × 10 = 5B/day ≈ 60k/s, peak 200k/s.
- Metadata per photo ~1 KB → 100 GB/day; sharded DB by user.
- CDN egress dominates: 5B feed loads × 10 images × 200 KB = 10 PB/day.
API
POST /v1/media/upload/init → presigned URL(s)
POST /v1/posts { mediaIds[], caption, tags[] }
GET /v1/feed?cursor=
GET /v1/users/{id}/posts?cursor=
POST /v1/posts/{id}/like / POST /v1/posts/{id}/comments
GET /v1/tags/{tag}/posts
Data model
posts (post_id, user_id, caption, created_at) -- sharded by user_id
media (media_id, post_id, original_key, variants:{thumb, medium, large}, width, height)
follows (follower_id, followee_id)
likes (post_id, user_id, ts) PK(post_id, user_id)
comments (post_id, comment_id, user_id, text, ts)
feed Redis LIST per user (post ids)
counters Redis: post:{id}:likes
Object storage keys: media/{media_id}/{variant}.jpg
High-level design
- Upload: client uploads the original directly to object storage via presigned URL; the media service enqueues a processing job.
- Image pipeline: workers generate variants (thumbnail 150, feed 640, full 1080), strip EXIF (privacy), run moderation/NSFW models, store variants, update
media. - Post service: creates the post once media is ready (or marks
processingand lets the feed skip it); emitspost.created. - Feed: same hybrid fan-out as a news feed — push post ids to followers' Redis lists, read-time merge for large accounts; hydrate metadata; image URLs point to the CDN.
- CDN: all variants served from a global CDN with long cache TTLs; image URLs are immutable (media id + variant), so no invalidation is needed.
- Engagement: likes/comments write to sharded tables + Redis counters; notification events published.
Architecture diagram
Deep dives
Immutable image URLs are the simplest CDN strategy: a new upload = new id; edits create new media. Cache TTL can be a year.
Variant generation on upload (eager) vs on first request (lazy): eager for the 3 standard sizes; lazy resize-on-the-fly (with caching) for unusual sizes.
Storage cost: originals to cold storage after 30 days; keep variants hot.
Privacy: strip location EXIF before storing variants; originals access-controlled.
Bottlenecks & mitigations
Image processing backlog at peak → autoscale workers; generate the feed variant first.
Feed fan-out for popular accounts → hybrid fan-out threshold.
Like counters on viral posts → sharded counters / batched increments.
CDN cache misses for long-tail content → origin shield; regional object-storage replicas.
Failure modes
- Processing worker fails: job retried; post stays
processing; feed skips it until ready. - CDN outage: multi-CDN or direct-to-origin fallback with rate limiting.
- Object storage regional outage: cross-region replication for variants; uploads fail over to another region.
- Feed cache loss: rebuild lazily from follows + posts DB.
Follow-up questions interviewers ask
How would you add Stories with 24h expiry?
How would you do image deduplication?
How would you build the Explore ranking?