← System design

Media & Streaming

Design YouTube / Netflix

A video platform is two systems that share a storage bucket. On one side is a factory that takes a raw upload and turns it into a ladder of resolutions and codecs cut into small segments, as fast and as cheaply as compute allows. On the other side is a delivery network that has to put the right segment on a screen in a living room within a couple of seconds, and keep doing it while the viewer's bandwidth rises and falls.
Core: segment-parallel transcoding + tiered CDN400 hours uploaded a minuteAbout 1,400 views per uploadPlayback starts in under 2 s

01

Scope

02

Scope

Functional
  • Creators upload videos up to 12 hours or 256 GB from browsers and phones, with resume after a dropped connection.
  • Every upload is converted into an adaptive bitrate ladder from 144p to 4K, plus thumbnails and auto captions.
  • Viewers press play on any device and quality adjusts automatically to their connection.
  • Seek to any point; resume where you left off on another device.
  • Show view counts, likes and a watch history per account.
  • Creators see processing status and can publish as public, unlisted or scheduled.
  • Uploads are scanned for copyright matches and policy violations before or soon after publishing.
Non-Functional
  • Time to first frame under 2 seconds at p90 on a typical broadband or 4G connection.
  • Rebuffering under 0.5% of watch time.
  • A 10-minute 1080p upload is watchable at some quality within 3 minutes, and fully processed within 15.
  • Uploaded originals are never lost: eleven nines of durability.
  • Playback stays available during the loss of a region or a CDN provider.
  • Delivery cost per hour watched keeps falling as the catalogue grows.
Which of the two are you designing?

The name joins two products with very different bottlenecks. Ask early which one the interviewer means, or say you will design for user uploads and mention where a studio catalogue would differ.

User-uploaded platform
  • Millions of new videos a day from anyone
  • Most videos are watched rarely; a few explode
  • Encoding must be fast and cheap per video
  • Spend expensive codecs only once a video is popular
  • Moderation and copyright matching at upload time
Subscription catalogue
  • A few thousand titles arrive a year from studios
  • Releases are known in advance, so caches are filled ahead
  • Encoding can take hours per title to save bandwidth
  • DRM and licensing windows per country
  • Delivery appliances live inside ISP networks

02

Rough numbers

03

Rough numbers

Assume 800 million daily viewers who watch about 45 minutes each, and creators who upload 400 hours of video every minute. The average upload is 10 minutes long and arrives at about 10 Mbps. Delivered streams average 2.5 Mbps once you mix phones and televisions.

QuantityWorkingResult
Hours uploaded per day400 h x 1,440 min576,000 h
Videos uploaded per day576,000 h x 6 per hourabout 3.5M
Original bytes per day576,000 h x 4.5 GB per hour at 10 Mbpsabout 2.6 PB
Rendition bytes per dayH.264 ladder about 9 Mbps total plus VP9 about 5.5 Mbps, so about 6.5 GB per hourabout 3.7 PB
New storage per year6.3 PB a day x 365, before redundancyabout 2.3 EB
Hours watched per day800M x 0.75 h600M h
Views per day800M x 6 videos4.8B, about 1,400 per upload
Egress per day600M h x 1.1 GB per hour at 2.5 Mbpsabout 675 PB
Delivery bandwidth675 PB x 8 / 86,400 s, peak 2xabout 62 Tbps, peak 125 Tbps
Encoding compute576,000 h x about 20 core-hours eachabout 480,000 cores busy around the clock
Segment requests at peakpeak concurrent streams about 50M, one 4 s segment each per 4 sabout 12.5M / s
What the table is telling you

Upload volume is large but steady, and it lands in object storage once. Delivery is two orders of magnitude bigger in bytes and has a person waiting on every request. No origin can serve 125 Tbps, so the architecture is mostly a plan for keeping segments as close to viewers as possible. The second big cost is encoding compute, which is why choosing when to spend expensive codecs matters.

03

API design

04

API design

POST/v1/uploadsOpen a resumable upload session
{ "filename": "trail-run-final.mov", "size_bytes": 5368709120, "sha256": "a91f...c0", "title": "Ridge run at dawn", "visibility": "scheduled", "publish_at": "2026-10-03T07:00:00Z" } // 201 { "video_id": "v_Qm8rT2xk", "session_url": "https://upload.example.net/s/9d1e...", "part_size": 16777216 // 16 MB parts }
PUT{session_url}?part=37Send one part; retry any part freely
// Content-Range: bytes 603979776-620756991/5368709120 // 200 { "received_parts": 37, "next_part": 38 } // After a crash, GET {session_url} returns which parts arrived
GET/v1/videos/{video_id}/statusProcessing progress for the creator
{ "state": "processing", "playable": ["360p", "720p"], "pending": ["1080p", "2160p"], "checks": { "copyright": "clear", "policy": "in_review" } }
GET/v1/playback/{video_id}?device=tv&drm=widevineEverything a player needs to start
{ "manifest": "https://c7.cdn.example.net/v_Qm8rT2xk/master.m3u8?tok=eyJ...", "fallback_manifests": ["https://alt-cdn.example.com/v_Qm8rT2xk/master.m3u8?tok=..."], "resume_at_s": 812, "license_url": null, // set for paid titles "token_expires_in": 21600 }
POST/v1/playback/{video_id}/heartbeatsProgress and quality beacons every 30 s
{ "position_s": 842, "rendition": "1080p", "rebuffer_ms": 0, "cdn": "c7" } // Feeds watch history, view counts and CDN steering; fire and forget

04

Architecture

05

Architecture

Upload and processing on the left, playback on the rightDiagram
flowchart LR
	CR["Creator app"] --> UP["Upload service"]
	UP --> RAW[("Originals bucket")]
	UP -. "upload complete" .-> Q[["Job queue"]]
	Q --> ORC["Pipeline orchestrator"]
	ORC --> ENC["Encoder fleet"]
	ENC --> REN[("Renditions bucket")]
	ORC --> META[("Video metadata")]
	V["Viewer player"] --> PB["Playback API"]
	PB --> META
	V --> EDGE["Edge cache in ISP"]
	EDGE -->|miss| MID["Regional shield cache"]
	MID -->|miss| REN
	V -. "heartbeats" .-> AN[["Events stream"]]

The write side is a pipeline. The upload service accepts parts directly into an originals bucket and, once the last part is verified against the file hash, drops a job on a queue. An orchestrator turns that job into a graph of tasks: inspect the file, split it into segments, encode each segment into each rendition, check quality, package and publish. Encoders are a large, mostly stateless fleet, much of it on cheap preemptible machines, because every task can simply be retried.

The read side barely touches our data centres. The playback API is a small service that checks permissions, picks a CDN and returns a signed manifest URL. From then on the player fetches a few kilobytes of manifest and then a steady stream of 4-second segments, almost all of them from an edge cache inside the viewer's ISP. Misses go to a regional shield that protects the origin, and only the shield ever reads the renditions bucket.

Upload service

Resumable sessions with fixed-size parts. Parts land in object storage in the nearest region, so the upload is as fast as the creator's link allows and a crash loses at most one part.

Pipeline orchestrator

Keeps the task graph per video in a durable workflow engine. It knows which segments are done, retries failures and publishes low renditions first so a video becomes playable early.

Encoder fleet

Workers pull segment tasks, encode with ffmpeg-style tooling or hardware encoders, and write output keyed by video, rendition and segment number. Identical inputs give identical outputs, so retries are safe.

Metadata store

Video records, rendition lists, ownership and visibility, served through a cache. View counts and likes live in a separate counter system so a viral video does not create a hot row.

CDN tiers

Edge caches in ISPs and internet exchanges, a regional shield layer, then origin. Popular titles are pushed to edges ahead of demand; the long tail is pulled through on first request.

Playback analytics

Heartbeats feed watch history, view counting with bot filtering, recommendations and quality monitoring that steers traffic away from a struggling CDN.

05

Deep dive: the encoding pipeline

06

Deep dive: the encoding pipeline

Why this is the expensive part

Encoding a single hour of 1080p into a full ladder in two codecs can take tens of CPU core-hours. Done as one job on one machine, a long video would take most of a day. Done carelessly at our upload rate, it would cost more than the delivery network. The pipeline has to be both parallel enough to be fast and selective enough to be cheap.

Split, encode in parallel, stitch

First the orchestrator inspects the original: duration, frame rate, resolution, audio tracks, whether it is HDR. Then it splits the video into chunks of roughly 10 seconds, cutting only at keyframes so each chunk decodes on its own. A 10-minute upload becomes 60 chunks, and each chunk becomes an independent task per output rendition. With 8 renditions that is 480 small tasks that can run on 480 machines at once, so the wall-clock time is bounded by the slowest chunk, not the length of the video.

Each encoder writes its output with forced keyframes every 2 seconds, so that every rendition has segment boundaries at exactly the same timestamps. That alignment is what lets a player switch from 720p to 1080p between two segments without a glitch. A packaging step then wraps the encoded chunks into CMAF segments of 4 seconds, which both HLS and DASH players can read, and writes one manifest per format that lists every rendition.

Task graph for one uploadDiagram
flowchart TD
	IN["Upload verified"] --> PR["Probe: duration, codecs, HDR"]
	PR --> SP["Split at keyframes into 10 s chunks"]
	SP --> FAST["Encode 360p and 720p H.264 first"]
	SP --> FULL["Encode 1080p and 4K H.264"]
	FAST --> PK1["Package and publish: playable"]
	FULL --> QC["Quality check per chunk"]
	QC --> PK2["Package full ladder"]
	PR --> AUX["Thumbnails, captions, audio fingerprint"]
	AUX --> MOD["Copyright and policy checks"]
	PK2 --> POP{"Watch time over threshold?"}
	POP -->|yes| VP9["Re-encode in VP9 or AV1"]
	POP -->|no| DONE["Keep H.264 only"]

The ladder we produce

Every upload gets an H.264 ladder, because every device can decode it. Higher-efficiency codecs are added later and only when a video earns them.

2160p
VP9 or AV1, popular only
about 14 Mbps
1440p
VP9
about 7 Mbps
1080p
H.264 high profile
about 4.5 Mbps
720p
H.264
about 2.5 Mbps
480p
H.264
about 1.1 Mbps
360p
H.264 main profile
about 0.6 Mbps
144p
H.264 baseline
about 0.1 Mbps

Spend compute where viewers are

Most uploads are watched a handful of times. Encoding all of them in AV1, which costs many times more CPU than H.264, would waste most of that work. So the pipeline is tiered. Every video gets the fast H.264 ladder. Once a video passes a watch-time threshold, a second pass re-encodes it in VP9 or AV1, which cuts the bytes per hour by 30 to 50%. For the few videos with very large audiences, it is worth going further: per-title or per-scene encoding runs trial encodes at several bitrates, scores them with a perceptual metric such as VMAF, and picks the cheapest bitrate per rung that still looks right. A cartoon may need half the bits of a handheld concert video at the same resolution.

Quality checks run on every chunk before packaging. Encoders occasionally produce corrupted output, especially on preemptible machines that are reclaimed mid-write. A chunk whose duration, frame count or quality score is off is re-encoded instead of being shipped to millions of screens.

Adaptive playback

The player downloads the manifest, starts at a conservative rung so the first frame appears quickly, and measures how fast each segment arrives. It keeps a buffer of 10 to 30 seconds and moves up the ladder when throughput and buffer allow, down when the buffer drains. Because segments are aligned across rungs, switching costs nothing but the next request. Starting fast matters more than starting sharp: viewers abandon a slow start far more than a soft first few seconds.

06

Key decisions

07

Key decisions

Chosen
Queue-driven pipeline with chunk-level tasks

Uploads return as soon as the bytes are safe. Hundreds of workers share one video, retries affect a single chunk, and the fleet can use preemptible capacity because losing a task is cheap.

Fast and cost-efficient
Rejected
Encode the whole file on one worker

Simpler to build, but a two-hour video takes hours to process, a crash at 90% starts over, and one machine type has to suit every file size.

Slow and fragile
Chosen
Push popular content, pull the long tail

Each night, predicted popular titles are copied into edge caches while links are idle, so the morning's demand is served without touching the shield. Everything else is cached on first request.

Best hit rate per byte
Alternative
Pure pull-through caching

No prediction needed and nothing wasted on titles nobody watches. But the first viewers of every hit wait for misses, and a surprise release can stampede the shield.

Fine for the long tail
Chosen
CMAF segments with HLS and DASH manifests

One set of media files serves both Apple devices and everything else. Only the small manifests differ, so storage and cache footprint are not doubled.

Half the storage of separate packaging
Rejected
Separate media files per protocol

Common before CMAF existed. It doubles bytes in storage and in every edge cache, and halves the effective hit rate.

Wasteful
Chosen
Expensive codecs only after a video is popular

Encoding cost follows watch time instead of upload volume. The first few hundred views use slightly more bandwidth, which is small next to the compute saved on millions of videos that never get those views.

Compute follows demand
Rejected
Full codec matrix for every upload

Every video looks its best from the first view, at several times the encoding bill. Most of that output would sit in storage and never be requested.

Pays for unwatched video
Chosen
Keep originals in cold, erasure-coded storage

Originals are read only when we re-encode, so they can sit in an archive tier with erasure coding at about 1.4x overhead. Renditions of active videos stay in a hot tier close to the shields.

Durable and cheap
Alternative
Discard originals after encoding

Saves 2.6 PB a day. But every future codec, resolution or quality fix would have to start from an already compressed rendition, and quality would degrade with each generation.

Tempting, usually regretted

07

Failure modes

08

Failure modes

💥
Encoder machines are reclaimed mid-task

A wave of preemptible instances is taken back by the cloud provider. Thousands of chunk tasks stop halfway, some after writing partial output files.

Tasks hold a lease with a heartbeat; when it lapses, the orchestrator reissues the task. Output is written to a temporary key and renamed only after a checksum, so a partial file is never treated as finished. Keep a slice of on-demand capacity for videos whose deadline is close.
🔥
A brand-new video goes viral

A creator with tens of millions of subscribers publishes. Within a minute, millions of players request the same first segments, none of which are cached at any edge yet.

Edges collapse concurrent misses into one upstream fetch, and the shield does the same, so the origin sees one request per segment per region. For scheduled premieres, push the first minutes of every rendition to edges before the publish time.
🧪
A corrupted chunk reaches viewers

A faulty encoder build produces green frames in some chunks. The pipeline publishes them and viewers see a flash of garbage at one point in the video.

Check duration, frame count and a quality score on every chunk before packaging. Canary new encoder versions on a small share of uploads, and keep enough metadata to re-encode just the affected chunks of every affected video.
🌐
A CDN or ISP cache cluster degrades

An edge site has a hardware fault or a congested link. Its viewers see rebuffering, but nothing on our servers is failing, so ordinary alarms stay quiet.

Use player heartbeats as the alarm: rebuffer ratio by CDN and by ISP. The playback API steers new sessions to a healthy site or a second CDN, and the manifest includes fallback hosts so active players can switch mid-stream.
📈
View counting melts a database row

A video gets 200,000 views a minute and each heartbeat tries to increment one row. The shard that owns it saturates and slows down playback API calls that read the same record.

Never count on the request path. Heartbeats go to an event stream, a streaming job filters bots and aggregates counts per minute, and the metadata cache shows a value that is a few minutes old.

08

Mistakes to avoid

⚠

Mistakes to avoid

🚫
Serving video from the application servers

Even a small platform needs terabits per second at peak. Application servers and a single region cannot carry that, and viewers far away get slow starts.

Instead: serve every segment from a multi-tier CDN and keep the origin behind a shield.
🚫
One file per resolution with no segments

The player cannot switch quality mid-stream, seeking needs range requests that cache poorly, and a network dip stalls playback.

Instead: cut every rendition into aligned segments of a few seconds and describe them in a manifest.
🚫
Making the creator wait for the full ladder

Waiting for 4K and the efficient codecs before anything is playable turns a three-minute wait into an hour for long videos.

Instead: publish low renditions first and add higher ones as they finish.

09

Interview advice

09

Interview advice

1

Split the problem in two right away. Say there is an encoding pipeline and a delivery network, with different bottlenecks, and ask which the interviewer wants to explore first.

2

Compute egress early. Turning watch hours into terabits per second makes the CDN obviously necessary. It is the single most persuasive number in this problem.

3

Explain why segments are aligned. Keyframes at fixed timestamps across every rendition are what make adaptive switching seamless. Few candidates say this and interviewers notice.

4

Parallelise by chunk, not by video. Show how a 10-minute upload becomes hundreds of independent tasks and how a crash loses one of them, not the whole job.

5

Talk about cost, not only speed. Mention tiered encoding where expensive codecs follow watch time, and per-title tuning for the biggest titles.

6

Describe the player. Buffer targets, starting at a low rung, and measuring throughput per segment show you understand that adaptive streaming is a client algorithm.

7

Keep counters off the hot path. View counts and likes are a classic hot-key trap. Use an event stream and aggregation, and accept a few minutes of lag.

8

Mention moderation and rights. Audio and video fingerprinting before publishing, and DRM for paid catalogues, show product awareness beyond plumbing.

10

How it grows

11

How it grows

Stage 1: a few hundred uploads a day
Upload, encode three sizes, serve from a bucket

One worker encodes each upload into 360p, 720p and 1080p with a single command. Files are served from object storage behind a commercial CDN. Simple and perfectly adequate at this size.

Stage 2: tens of thousands a day
Queue, worker pool and segmented streams

Put a queue between upload and encoding, add a worker pool, and switch to segmented HLS so players can adapt. Resumable uploads arrive because long videos keep failing.

Stage 3: millions a day
Chunk-level parallelism and an orchestrator

Whole-file encoding is too slow for long uploads. Split at keyframes, encode chunks in parallel, publish low renditions first and add quality checks.

Stage 4: delivery cost dominates
Efficient codecs and caches inside ISPs

Bandwidth is now the largest bill. Add VP9 and AV1 for popular videos, per-title tuning for the biggest, and place cache appliances inside ISP networks filled overnight.

Stage 5: global and multi-CDN
Steering by measured quality

Several CDNs and the in-house edge are chosen per session from live player telemetry, and the pipeline runs in multiple regions close to creators.

Watch and read

Try next

Free to read · better with landed

Whiteboard this with landed

landed runs it as a live system design round on the whiteboard and grades your trade-offs.