Design Instagram — a photo sharing social media platform
Instagram is a photo and video sharing platform whose systems are dominated by three engineering challenges: a high-throughput media ingestion pipeline, a globally distributed read-heavy feed, and media delivery at internet scale. With over a billion users and hundreds of millions of daily active users uploading and viewing photos and videos, the system must handle enormous write bursts, cache aggressively, and serve media from CDN edge nodes within milliseconds of a user's location anywhere in the world.
Functional and Non-Functional Requirements
Functional requirements: upload a photo or video with captions, hashtags, and location; follow users; view a personalized home feed; like and comment; watch Stories (24-hour ephemeral posts); send Direct Messages; search by hashtag and username; browse the Explore tab (recommendations). Non-functional requirements: sub-200ms feed load at p99, 99.99% availability, eventual consistency on post visibility (seconds, not milliseconds), and support for hundreds of millions of concurrent sessions globally.
Media Upload Pipeline
The upload path is designed to minimize load on the application servers:
Client-side pre-processing — the mobile app compresses and resizes the image to a standard base resolution before uploading, reducing upload payload size by 50–80%.
Signed URL upload to S3 — the upload service issues a pre-signed S3 URL; the client PUTs the media directly to object storage, bypassing the API servers entirely and saving CPU and bandwidth at the application tier.
Async post-processing pipeline — an S3 event notification triggers a processing pipeline (
AWS Lambdaor a dedicated service) that generates multiple thumbnail variants (150px, 320px, 640px, 1080px), applies any server-side quality enhancement or content moderation ML, and writes all variant keys to the post record.Post metadata storage — once processing completes, the service writes the post record
(post_id, user_id, s3_keys[], caption, location, hashtags, created_at)to a sharded relational store partitioned byuser_id.Fan-out event — a post-created event is published to Kafka, triggering fan-out workers, search indexing, hashtag counter updates, and notification dispatch.
Feed Architecture: Hybrid Fan-Out
Instagram's feed uses the same hybrid fan-out pattern as Twitter. Fan-out on write for regular users: when a user with fewer than ~1M followers posts, a worker writes the post ID into the Redis sorted set for each follower, keyed by user_id with score = timestamp. Fan-out on read for celebrities and large accounts: their posts are pulled in at request time and merged with the precomputed feed. The feed service fetches the top 500 post IDs from Redis, hydrates them in parallel from the post store, applies a ranking model (engagement prediction, recency, relationship strength, content diversity), and returns the final ranked list. Media URLs in the response point to the CDN, not S3 directly.
Media Delivery via CDN
All media is served through a global CDN (CloudFront, Akamai, or a proprietary PoP network). Image URLs are content-addressed (the URL contains a hash of the content), allowing the CDN to cache them with an effectively infinite TTL. The API never returns raw image bytes — it returns CDN URLs. The client's media is prefetched aggressively: the feed service sends a few posts ahead of what is currently visible so that scrolling feels instant even on a 4G connection.
Key trade-off — denormalized counters vs consistent counts: Like counts, follower counts, and comment counts on popular posts can receive thousands of increments per second. Writing each increment directly to the post row creates a hot-row contention problem in any SQL database. The solution is to buffer increments in Redis (atomic
INCRon a per-post counter key) and flush to durable storage periodically in batch. This means the displayed count may be a few seconds stale, but that is acceptable — nobody notices a count of "4,231,187" vs "4,231,194".
Stories, Direct Messages, and Explore
Stories — ephemeral posts with a 24-hour TTL. They use the same media upload path (S3 + CDN) but with TTL metadata set on both S3 (lifecycle policy) and the metadata database. A background sweeper marks stories as expired and eventually deletes the S3 objects. Clients preload the Stories of followed users in the background.
Direct Messages (DMs) — reuse the chat system design: WebSocket gateways per region, Cassandra-backed per-conversation message store, APNs/FCM for offline delivery.
Search — hashtags and usernames are indexed in Elasticsearch with near-real-time indexing triggered by Kafka consumers. Typeahead suggestions use a separate in-memory prefix-trie service.
Explore (Recommendations) — a separate offline ML pipeline computes personalized post recommendations based on a user's interaction history, collaborative filtering signals, and trending content; results are precomputed and cached in Redis per user.
The strongest differentiator in this design is explaining the signed-URL direct upload pattern (client → S3, bypassing app servers) and why it matters at scale — it is easy to overlook and clearly signals production system knowledge. Connect the media pipeline, fan-out, and CDN into a coherent data flow rather than listing components in isolation.
Mentioning Redis INCR batching for like-count hot rows is a concrete detail that stands out. For Stories, tie the 24-hour TTL to both S3 lifecycle policies and database TTL to show you've thought through cleanup.