Vetora logo
Hard12 componentsInterview: Very High

Instagram — CDN-First Photo Sharing

Design an Instagram-style photo sharing platform with presigned URL uploads, async image processing into multiple variants, Redis-cached feeds, and global CDN delivery for sub-100ms image latency.

cdnobject-storageasync-processingsocial-media
Problem Statement

Designing an Instagram-like photo sharing platform is one of the most frequently asked system design interview questions across top technology companies. The problem exercises a wide range of distributed systems concepts: media upload pipelines, asynchronous processing, content delivery networks, feed generation, and caching at massive scale. Interviewers expect candidates to reason about the fundamental tension between write-path complexity (uploading and processing images) and read-path performance (serving feeds with embedded media to hundreds of millions of users).

The real Instagram handles over 100 million photos uploaded per day, serves feeds to more than 500 million daily active users, and delivers billions of image requests globally. At this scale, every architectural decision has enormous cost and performance implications. Proxying image bytes through API servers, for example, would require bandwidth measured in terabits per second. Serving feeds from the database on every request would demand millions of complex queries per second. These constraints force the architecture toward presigned URL uploads, asynchronous image processing, and aggressive CDN caching.

The core challenges include handling media uploads without overwhelming the API tier, resizing images into multiple variants (thumbnail, medium, large) without blocking the upload response, distributing images globally with sub-100ms latency, pre-computing personalized feeds so that read latency stays low, and managing the fan-out problem where a celebrity post must reach millions of followers. Each of these challenges has well-established solutions, but integrating them into a coherent architecture requires careful reasoning about data flow and consistency trade-offs.

Candidates who can articulate the separation of read and write paths, explain why CDN is the most critical component for read performance, and reason about eventual consistency trade-offs on new posts will demonstrate strong system design intuition.

Architecture Overview

The architecture separates the read path from the write path with independent services that scale according to their distinct traffic profiles. Two client types represent the workload split: ReadClient generates 85% of traffic (feed reads and post details), while UploadClient generates 15% (media uploads, post creation, and interactions like likes and comments). An API Gateway handles authentication and rate limiting at 150K RPS, routing all traffic through an Application Load Balancer to the appropriate service.

The write path centers on PostService, which handles media uploads via presigned S3 URLs, post creation, and user interactions. When a user uploads a photo, PostService generates a presigned URL in approximately 2ms and returns it to the client. The client then uploads the 2MB image directly to S3, completely bypassing the API servers. This is critical at scale: proxying 2MB images at 5K uploads per second would consume 10GB/sec of bandwidth through the API tier. After upload, PostService publishes an event to an image processing Kafka topic, and ImageWorker asynchronously creates three resized variants (150px thumbnail, 600px medium, 1080px large) using libvips, then pushes them to S3 where CloudFront CDN serves them at edge locations worldwide.

The read path is served by FeedReader, a 12-pod service optimized for cache lookups. User feeds are pre-computed and stored in a 6-node Redis cluster (ElastiCache) with a 92% cache hit rate. Each feed entry contains post metadata with CDN URLs for image variants, so the client fetches actual image bytes directly from CloudFront rather than through the API. On cache miss, FeedReader falls back to PostgreSQL. The CDN layer is the most critical read-path component, absorbing 95% of image traffic with a long TTL on immutable media.

The data layer includes PostgreSQL (32 partitions, 3 replicas) as the source of truth for posts, users, and follow relationships, plus the Redis feed cache and S3 object storage for media. The async image processing pipeline uses Kafka with 16 partitions for worker parallelism, with each image taking approximately 300ms to process across all three variants.

Architecture Preview
Loading architecture preview...
Key Design Decisions
Presigned URL Upload

Choice

Client uploads directly to S3 via presigned URL

Rationale

At 5K uploads per second with an average image size of 2MB, proxying through the API tier would consume 10GB/sec of bandwidth and memory on the API servers. Presigned URLs let the client upload directly to S3, and the API server only pays the 2ms cost of signing the URL. This is Instagram's actual production pattern and eliminates the API tier as a bottleneck for media ingestion.

Async Image Processing

Choice

Kafka-driven worker pipeline for image resizing

Rationale

Resizing an image into three variants (thumbnail, medium, large) takes 200-500ms per image. Processing synchronously in the upload path would push upload latency to 700ms or more. The Kafka-based async pipeline means the upload completes in approximately 100ms, and variants are ready within seconds. The brief window where a post is visible but variants are still processing is handled by client-side loading placeholders.

CDN for Global Image Delivery

Choice

CloudFront with S3 origin and long TTL on immutable media

Rationale

Without CDN, all image traffic hits the S3 origin. At 500K feed reads per second, with 5 images per feed and 200KB per image, that would be roughly 500 Tbps of origin traffic. CloudFront edge caching absorbs 95% of image requests, delivering images in under 100ms globally from 200+ edge locations. CDN is the single most impactful component for read-path performance at Instagram scale.

Redis Feed Cache

Choice

Pre-computed per-user feeds in Redis with LRANGE reads

Rationale

Feed reads represent 70% of total traffic at 70K RPS peak. Pre-computing feeds as lists of post IDs with CDN URLs in Redis enables O(1) feed reads via LRANGE. At a 92% cache hit rate, only about 5,600 reads per second fall through to the database. Without the cache, PostgreSQL would need to handle 70K complex feed join queries per second, which is infeasible at this scale.

Scale & Performance

Target RPS

100K RPS peak (70K feed reads, 15K post detail, 15K writes)

Latency (p99)

p99 < 500ms for feed metadata; p99 < 100ms for CDN image delivery

Storage

~150 TB/day S3 growth (3 variants per post across 100M daily uploads); 18 GB Redis feed cache

Availability

99.95% with multi-AZ deployment and CDN edge failover

This template is for educational and illustration purposes only. It may not represent the optimal production design for this problem. Real-world systems involve additional considerations (compliance, specific cloud provider constraints, organizational requirements) not captured here. Use this as a starting point for discussion, not as a production blueprint.

Frequently Asked Questions
Why does the API server never touch image bytes?

At Instagram scale, image uploads generate enormous bandwidth. With 5,000 uploads per second and an average image size of 2MB, the aggregate upload bandwidth is 10GB per second. Routing this through API servers would require massive network capacity on the API tier, increase memory pressure from buffering large payloads, and add unnecessary latency. Presigned URLs allow the client to upload directly to S3, and the API server only generates the signed URL in about 2ms. This pattern is used by Instagram, Dropbox, and most large-scale media platforms in production.

How does the system handle the feed fan-out problem for celebrity accounts?

This template uses a simple push-based model where new posts are written to each follower's feed cache in Redis. For a user with 1,000 followers, this means 1,000 Redis writes per post, which is manageable. However, a celebrity with 100 million followers would trigger 100 million cache writes per post, which is impractical. Production Instagram uses a hybrid fan-out approach: push for normal users (pre-compute feeds) and pull for celebrities (compute on read by merging the celebrity timeline into the feed at query time). This whale-aware strategy is covered in the feed-ranking template variant.

What happens if the image processing worker fails mid-resize?

Kafka provides at-least-once delivery semantics. If an ImageWorker crashes while processing, the message is not acknowledged and Kafka redelivers it to another worker. The worker pipeline is idempotent because uploading the same resized variant to S3 with the same key simply overwrites the object. In the worst case, a user sees a loading placeholder for their image slightly longer than the typical 10-second processing window. Failed processing events are retried up to three times before being sent to a dead-letter queue for manual investigation.

Why are three image variants created instead of serving a single high-resolution image?

Mobile screens range from 320px to 4K resolution. Serving a single 4K image to a device that only needs a 150px thumbnail wastes bandwidth dramatically, with a 5MB image versus a 15KB thumbnail. Three variants (150px thumbnail at approximately 15KB, 600px medium at approximately 100KB, and 1080px large at approximately 400KB) let the client request the appropriate size for its context. This reduces CDN bandwidth costs by 5-10x compared to serving full-resolution images everywhere, and significantly improves load times on mobile networks.

How does the feed cache stay consistent when new posts are created?

Feed consistency is eventual. When a user creates a post, PostService writes the post to PostgreSQL and then pushes the post metadata to each follower's feed list in Redis. There is a brief window (typically under 1 second) where the post exists in the database but has not yet appeared in all followers' caches. Additionally, image variants may not be ready for up to 10 seconds after upload because of async processing, so CDN URLs may 404 briefly. The client handles this with loading placeholders. This eventual consistency trade-off is acceptable for a social media feed where users do not expect real-time delivery of every post.

Related Templates

Discussion

Sign in to join the discussion.

Ready to design your own Instagram?

Open the simulator, place components on the canvas, wire them up, and run a traffic simulation to see how your architecture performs under real load.

Open Simulator