Design YouTube (Video Streaming)
A video platform (YouTube, Netflix, TikTok) lets anyone upload a video and lets everyone else watch it smoothly on any device over any network. On the surface it looks like the news feed turned up — extremely read-heavy, globally distributed. But video changes the shape of the problem: the “records” are gigabyte blobs, not kilobyte rows, so the system is bandwidth-bound, not QPS-bound. The dominant cost isn’t database queries — it’s pushing petabytes out of CDNs every day. We’ll follow the framework.
1. Requirements
Section titled “1. Requirements”Functional
- Upload a video (large file, resumable).
- Transcode it into multiple resolutions/bitrates for playback on any device and network.
- Stream playback that adapts to changing bandwidth without buffering.
- Browse/search, view counts, likes, comments.
Non-functional
- Massively read-heavy — a popular video is uploaded once and watched billions of times.
- Smooth playback — fast startup (low time-to-first-frame) and no stalls mid-stream.
- Durability — never lose an uploaded original.
- Global low latency — a viewer in São Paulo and one in Seoul both start instantly.
2. Back-of-envelope estimation
Section titled “2. Back-of-envelope estimation”Two numbers matter: how much we ingest, and how much we serve.
INGEST (illustrative, hedged — figures drift) uploads: ~500 hours of video uploaded per minute (oft-cited, ~recent years) = 500 × 60 × 24 ≈ 720,000 hours/day ingested each source hour fans out into a "ladder" of ~6 renditions → multiplies stored bytes
EGRESS (this is the whole system) watch time: ~1 billion hours watched/day (oft-cited) avg concurrent: 1e9 hours ÷ 24 h ≈ 42M streams being watched at any instant per stream: ~2.5 Mbps average (mixed mobile/desktop) egress: 42e6 × 2.5e6 bits/s ≈ 1.0e14 bits/s ≈ ~100 Tbps average peak 2–3× → hundreds of TbpsA hundred-plus terabits per second, sustained, is a quantity no origin datacenter serves directly. It is served from thousands of CDN edge caches close to viewers. The origin’s job shrinks to producing renditions and seeding the CDN; the CDN does the heavy lifting. Compare the modest write path (720k upload-hours/day) against the colossal read path — the asymmetry is the design.
3. API sketch
Section titled “3. API sketch”Uploads use a resumable protocol because a multi-gigabyte POST that dies at 90% must not restart from zero.
POST /v1/videos → { video_id, upload_url } // reserve, get resumable URLPUT {upload_url} (Content-Range: bytes 0-...) // chunked, resumablePOST /v1/videos/{id}/complete → { status: "processing" } // triggers transcode pipeline
GET /v1/videos/{id} → { title, manifest_url, ... } // metadata + playback manifestGET /v1/videos/{id}/master.m3u8 → ABR manifest (variant list) // served from CDNPOST /v1/videos/{id}/view → 204 // fire-and-forget view eventThe client never asks for “the video file.” It fetches a manifest that lists the available bitrate renditions, then pulls small segments on demand — the heart of adaptive streaming.
4. Data model
Section titled “4. Data model”The defining split: metadata in a database, bytes in object storage.
videos (SQL/NoSQL row — small) segments (object storage — huge blobs) video_id (PK) s3://bucket/{video_id}/1080p/seg_0001.ts uploader_id s3://bucket/{video_id}/720p/seg_0001.ts title, description ... (immutable, CDN-cacheable) status (processing|ready) manifest_url view_counts (write-heavy counter — see §6) duration, created_at video_id (PK) count (approximate, batched)Never put video bytes in a relational database. The metadata (titles, owners, status) is small, queried richly, and belongs in a database; the media is enormous, immutable once transcoded, and belongs in object storage fronted by a CDN. Segments are content-addressable and cacheable forever — exactly what an edge cache wants.
5. High-level design — the transcoding pipeline
Section titled “5. High-level design — the transcoding pipeline” UPLOAD ─► resumable upload service ─► raw original ─► OBJECT STORE (source of truth, durable) │ ▼ enqueue transcode job ┌────────────────┐ │ Message queue │ (split into chunks) └───────┬────────┘ ┌───────────────┬───────┴───────┬───────────────┐ ▼ ▼ ▼ ▼ transcode transcode transcode transcode (worker fleet) chunk→144p..4K ... ... ... │ package into HLS/DASH segments + manifests ▼ packaged renditions ─► seed CDN edges ─► READY │ PLAYBACK: player ─► GET master manifest (CDN) ─► picks bitrate ─► GET segments (CDN) ─► decodeTranscoding is embarrassingly parallel: split the source into independent chunks, fan them out to
a worker fleet through a message queue, transcode each into the full
bitrate ladder in parallel, then stitch the segments and write manifests. The poster gets an instant
processing response; the video appears once the pipeline finishes. This is the same async-fan-out
pattern as the web crawler — a queue decouples the slow,
spiky work from the fast request.
Under the hood — ABR, HLS, and DASH
Section titled “Under the hood — ABR, HLS, and DASH”Adaptive bitrate (ABR) is how playback survives a flaky network. The transcoder produces the same video at several resolutions/bitrates — the bitrate ladder (e.g. 144p, 240p, 360p, 480p, 720p, 1080p, 4K), and chops each into short segments (typically 2–10 seconds). A manifest lists them:
master.m3u8 (HLS) or .mpd (DASH) ← lists the bitrate variants ├── 240p/index.m3u8 → seg_0001.ts, seg_0002.ts, ... ├── 720p/index.m3u8 → seg_0001.ts, seg_0002.ts, ... └── 1080p/index.m3u8 → seg_0001.ts, seg_0002.ts, ...The client measures its download speed and buffer level and picks the next segment’s bitrate on
the fly — bandwidth drops, it grabs the 480p segment instead of 1080p; bandwidth recovers, it climbs
back up. HLS (HTTP Live Streaming, from Apple, .m3u8) and MPEG-DASH (.mpd, an ISO standard)
are the two dominant ABR protocols; both ride plain HTTP, which is precisely why ordinary CDNs can
cache the segments. Segments are just files — no special streaming server required.
6. Counting views without melting a counter
Section titled “6. Counting views without melting a counter”A viral video gets millions of view events per minute, all incrementing one row — a textbook
hot partition. A synchronous UPDATE ... SET count = count + 1 on a single key would serialize behind a lock and collapse. Instead:
- Fire-and-forget view events into a queue; the player doesn’t wait.
- Batch + aggregate — workers count events in windows and apply periodic bulk increments, so one row gets a few writes per second, not millions.
- Approximate is fine — the displayed count can lag and be probabilistic; nobody needs the 4,001,237th view to be exact in real time. This is eventual consistency, traded deliberately for write throughput.
Key trade-offs
Section titled “Key trade-offs”- CDN vs origin: the CDN buys low-latency global delivery and slashed origin bandwidth; it costs egress fees and only helps popular (cacheable) content — the long tail still hits origin.
- Pre-transcode every rendition vs transcode on demand: pre-computing the full ladder buys instant, cache-friendly playback at the cost of storage and compute spent on renditions nobody may watch; on-demand saves storage but adds first-view latency.
- Bytes in object store vs database: separating media (blobs) from metadata (rows) buys the right tool for each — cheap durable storage vs rich queries — at the cost of a two-system data model.
- Exact vs approximate view counts: approximate, batched counting buys write throughput on hot videos; it costs real-time precision nobody actually needs.
Check your understanding
Section titled “Check your understanding”- Why is a video platform described as bandwidth-bound rather than QPS-bound, and how does that change the design compared to a news feed?
- Walk through the transcoding pipeline. Why is it modeled as parallel chunks behind a queue?
- What is adaptive bitrate streaming, and what role do the manifest and segments play? Why can an ordinary HTTP CDN cache them?
- Why store video bytes in object storage but metadata in a database?
- A single viral video receives millions of view events per minute. Why would a synchronous counter fail, and what three techniques fix it?
Show answers
- Each read ships megabytes per second of video, so the limiting resource is egress bandwidth, not request rate. A news feed serves tiny rows and is designed around databases, caches, and replicas; a video platform serves huge immutable blobs and is designed around CDNs and pre-computed renditions — ~100 Tbps of average egress is served from thousands of edge caches, not from origin databases.
- Upload lands the raw original in durable object storage, which enqueues a transcode job; workers
split the source into independent chunks, transcode each into the full bitrate ladder in parallel,
then package HLS/DASH segments + manifests and seed the CDN. It’s modeled as parallel chunks behind a
queue because transcoding is embarrassingly parallel and slow — the
queue decouples the spiky, expensive work from the instant
processingresponse the uploader gets. - ABR produces the same video at several bitrates (the ladder), each chopped into short segments;
a manifest lists the variants and their segments. The client measures bandwidth/buffer and picks
each next segment’s bitrate on the fly, climbing down on congestion and back up on recovery. Because
HLS (
.m3u8) and DASH (.mpd) ride plain HTTP and segments are just files, ordinary CDNs cache them — no special streaming server needed. - Media is enormous, immutable once transcoded, and best served cheaply and durably from object storage fronted by a CDN; metadata (titles, owners, status) is small and richly queried, which is what a database is for. Putting gigabyte blobs in a relational DB would be slow, costly, and pointless. The cost of the split is maintaining a two-system data model.
- All those events increment one row — a hot partition that serializes behind a lock and collapses
under synchronous
+1writes. Fix: fire-and-forget events into a queue (player doesn’t wait), batch/aggregate increments in windows so the row sees a few writes/sec, and accept an approximate, eventually-consistent count — trading real-time precision (which nobody needs) for write throughput.