Designing a Video Streaming Platform
How Netflix and YouTube store, encode, and deliver video to 200M concurrent viewers — adaptive bitrate streaming, CDN architecture, and the encoding pipeline.
Like explaining to a 5-year-old: Imagine a library with one really popular book. If everyone had to drive to the library to read it, there’d be a huge traffic jam. So the library makes copies and puts them in local branches all over the city. When you want the book, you go to the closest branch, not the main one. That’s a CDN — copies of videos stored close to where you live, so they load fast.
Netflix serves 15% of all internet traffic. YouTube processes 500 hours of video per minute. The secret isn’t one giant server — it’s a carefully designed pipeline from upload to playback that treats video as a content distribution problem, not a file storage problem.
Requirements
Functional
- Upload videos (up to 4K, up to 4 hours long)
- Stream video with adaptive quality based on connection speed
- Support pause, resume, seek to any position
- Search, recommendations, watch history
- Comments, likes, subscriptions
Non-Functional
- Scale: 200M concurrent viewers, 500 hours of video uploaded per minute
- Latency: video must start playing within 2 seconds of clicking play
- Availability: 99.99% — a streaming outage is immediately visible to millions
- Storage: petabytes of video, retained indefinitely
Capacity Estimation
| Metric | Value |
|---|---|
| Videos uploaded/day | ~720K (500hr/min) |
| Avg video size (raw 1080p, 1hr) | ~3.5 GB |
| Storage for uploads/day | ~2.5 PB |
| After encoding (4 quality levels, avg 3x compression) | ~8 PB/day |
| Bandwidth (200M viewers × 5 Mbps avg) | ~1 Tbps |
1 Tbps of outbound bandwidth — you need a CDN. No single origin can serve this.
The Upload & Encoding Pipeline
The photograph analogy: When a professional photographer takes a raw photo, they have to “develop” it into different formats — a full-resolution print, a web-sized JPEG, a thumbnail. Video encoding is the same: one raw upload gets “developed” into multiple resolutions and codecs so every device gets the right version.
Creator uploads raw video
│
▼
┌──────────────┐
│ Upload API │ ← chunked upload, resumable (TUS protocol)
└──────┬───────┘
│ store raw
▼
┌──────────────┐
│ Object │ ← S3/GCS raw storage (warm tier)
│ Storage │
└──────┬───────┘
│ trigger
▼
┌──────────────────────────────────────────────────┐
│ Encoding Cluster │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │
│ │ 360p │ │ 720p │ │ 4K HDR │ │
│ │ H.264 │ │ H.264 │ │ HEVC/AV1 │ │
│ └──────────┘ └──────────┘ └────────────────┘ │
│ │
│ Also generates: thumbnails, subtitles, chapters │
└──────────────────────────────────────────────────┘
│ push encoded segments
▼
┌──────────────┐
│ CDN Origin │ ← encoded segments stored here
└──────┬───────┘
│ replicate to edges
▼
┌──────────────┐
│ CDN Edges │ ← ~1,000 PoPs worldwide (Akamai, Cloudflare, or Netflix OCA)
└──────────────┘
Segmented Video: HLS and DASH
Video isn’t served as one giant file. It’s split into 2-10 second segments:
video.m3u8 (HLS manifest)
├── 360p/
│ ├── seg_001.ts
│ ├── seg_002.ts
│ └── ...
├── 720p/
│ ├── seg_001.ts
│ └── ...
└── 1080p/
├── seg_001.ts
└── ...
The manifest file tells the player where all segments are. When you seek to 45 minutes in, the player just requests the segment containing that timestamp — no need to download the first 44 minutes.
Adaptive Bitrate Streaming (ABR)
The highway analogy: Imagine a highway that has 4 lanes. When traffic is light, you drive in the fast lane (4K). When traffic builds up, you move to a slower lane (720p). You’re still moving forward without stopping — you just adjust your speed. That’s adaptive bitrate: the player switches quality to keep playback smooth.
The video player monitors:
- Download speed of the last 3 segments
- Buffer health (how many seconds of video are pre-loaded)
- Screen resolution of the device
Based on these signals, it selects the appropriate quality level for the next segment:
def select_quality(bandwidth_bps: float, buffer_seconds: float, screen_height: int):
if buffer_seconds < 5 or bandwidth_bps < 1_000_000:
return "360p" # low buffer: protect against stall
if bandwidth_bps < 3_000_000 or screen_height <= 720:
return "720p"
if bandwidth_bps < 15_000_000:
return "1080p"
return "4K"
The player never downloads the video you’re not currently watching. If you jump to the end, it immediately starts fetching from that segment forward.
CDN Architecture
Netflix’s CDN strategy is so important they built their own: Open Connect Appliances (OCAs) — custom servers deployed directly inside ISP data centers.
Viewer in Mumbai
│
▼ DNS resolves to nearest PoP
CDN Edge (Mumbai ISP data center)
│ cache hit?
├──▶ YES: serve segment directly (< 10ms)
│
└──▶ NO: fetch from CDN origin, cache it
(then future viewers in Mumbai get cache hit)
Cache hit rates for popular content: ~95%. Most people watch the same top 10K videos.
For long-tail content (old or unpopular videos), serve from origin directly.
Database Design
Metadata (PostgreSQL or MySQL)
CREATE TABLE videos (
video_id BIGINT PRIMARY KEY,
creator_id BIGINT NOT NULL,
title TEXT NOT NULL,
description TEXT,
duration_secs INT,
status VARCHAR(20) DEFAULT 'processing', -- processing|ready|removed
created_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE video_manifests (
video_id BIGINT REFERENCES videos(video_id),
quality VARCHAR(10), -- 360p, 720p, 1080p, 4K
manifest_url TEXT,
size_bytes BIGINT
);
Watch History (Cassandra)
-- Cassandra: partition by user_id, cluster by watch timestamp
CREATE TABLE watch_history (
user_id UUID,
watched_at TIMEUUID,
video_id UUID,
position_s INT, -- resume point
PRIMARY KEY (user_id, watched_at)
) WITH CLUSTERING ORDER BY (watched_at DESC);
Video Counters (Redis + async flush to DB)
INCR views:{video_id} -- atomic counter
ZADD trending 1234567 video_id -- trending sorted set
Don’t update SQL on every view — buffer in Redis and flush to DB in batches every minute.
Search
Video search is not full-text SQL search at scale. The pipeline:
- On upload: extract title, description, auto-generated captions (via speech-to-text)
- Index in Elasticsearch: title, tags, transcript snippets
- On search: query Elasticsearch for candidate videos, then re-rank by personalization signals
Recommendation Engine
Collaborative filtering: “users who watched X also watched Y.”
At YouTube’s scale, this is a two-stage neural network:
- Candidate generation: narrow 1 billion videos to ~hundreds based on embedding similarity
- Ranking: score the hundreds by watch probability given this specific user’s context
For an interview: describe the two-stage approach and mention that watch history is represented as user embeddings in a vector database.
Trade-offs & Decisions
| Decision | Choice | Reason |
|---|---|---|
| Video format | HLS/DASH segments | Enables seeking, adaptive quality, CDN cacheability |
| CDN | Custom edge network | Cost at scale; ISP co-location reduces latency dramatically |
| Encoding | Distributed job cluster | 500hr/min can’t be done on one machine; horizontal scale |
| Watch counters | Redis buffered → DB flush | Avoid write amplification on hot videos |
| Metadata storage | PostgreSQL | Structured, joins needed; manageable at metadata scale |
Video streaming is fundamentally a content distribution problem. The encoding pipeline and CDN architecture do 95% of the work — your application servers are almost an afterthought. The hard questions are always about cost: how long do you store raw uploads? When do you evict long-tail segments from CDN edges?
You might also like
Designing a Web Crawler
How Google indexes the internet — distributed BFS across billions of pages, politeness constraints, content deduplication, and the surprisingly hard problem of deciding what to crawl next.
Designing a Distributed Key-Value Store
Building a Redis or DynamoDB from scratch — consistent hashing, replication, the CAP theorem, quorum reads and writes, and conflict resolution with vector clocks.
Designing a News Feed (Twitter/Instagram)
How social platforms generate personalized feeds for hundreds of millions of users — fan-out strategies, ranking signals, and the push vs pull debate.