Design Video Streaming

Design video upload and playback: resumable chunked uploads, a transcoding job queue, adaptive bitrate with a manifest, and CDN delivery.

Case study32 minLesson 2 of 8
On this page
  1. Clarify the problem
  2. Estimate the scale
  3. Define the API
  4. Model the data
  5. High-level design
  6. Deep dives
  7. Bottlenecks and failures
  8. What a strong answer sounds like
  9. Follow-up questions

Someone uploads a 10-minute video from their phone on a shaky train connection. A few minutes later it plays on a TV in full HD, on a laptop in a café, and on a phone in a lift that drops to a lower quality for a few seconds instead of freezing. That is a video streaming service.

Interviewers ask it because the bytes dominate everything. One popular video moves more data in an hour than the metadata database handles in a year, so the design must split the two cleanly: a small, careful system for titles and status, and a huge, dumb, cached system for bytes. The steps follow A Framework for Any Design.

Clarify the problem

The first five minutes
Interviewer

Design a video streaming service.

You

User uploads and on-demand playback, or live streams too? And are we covering search, comments and recommendations?

Interviewer

Uploads and on-demand playback only. Skip live, search and recommendations.

You

How many uploads a day, how long are they, and how much do people watch?

Interviewer

About 100,000 uploads a day, 10 minutes on average. 50 million daily viewers, about 30 minutes each.

You

Then playback is the system: it must start within about 2 seconds and rarely stall, on any device and network. Uploads must survive bad connections, and a processed video can be ready a few minutes after upload. An uploaded video is never lost. Most of the cost will be bandwidth, so I'll design around the CDN.

  • Functional: upload a video (resumable); process it into formats every device can play; play it with quality that adapts to the network; show title, status and view count.
  • Non-functional: playback starts in about 2 seconds and stalls rarely; playback is highly available; videos are ready within minutes of upload; uploaded files are never lost; metadata may be eventually consistent (a view count can lag).
  • Out of scope: live streaming, search, recommendations, comments, copy protection.

Estimate the scale

Start with what viewers do, because that is where the bytes are.

  • Concurrent viewers: 50 million × 30 minutes ÷ 1,440 minutes a day ≈ 1 million watching at any moment on average. Assume an evening peak of 2x: 2 million.
  • Uploads: 100,000 ÷ 86,400 s ≈ 1.2 a second. Assume an original file averages 1 GB: 100 TB a day of uploads, about 1.2 GB/s coming in.
  • Processed copies: five qualities from 240p to 1080p at 0.4 + 0.8 + 1.5 + 3 + 6 = 11.7 Mbit/s together. For 10 minutes: 11.7 × 106 × 600 ÷ 8 ≈ 0.88 GB per video, about 88 TB a day.
  • Storage per year: (100 + 88) TB × 365 ≈ 69 PB before replication. Originals are rarely read again, so they can move to cheaper, slower storage.
  • Metadata: 100,000 × 365 = 36.5 million new video rows a year at about 1 KB each: 36.5 GB. Tiny next to the bytes.
Estimate: Playback bandwidth

At the average of 1 million concurrent viewers, how much video leaves the system each second?

You know
  • 1 million viewers at any moment
  • Average delivered bitrate 4 Mbit/s (a mix of phones and TVs)
  • 8 bits in a byte

Show the working
  1. 106 × 4 × 106 bit/s = 4 × 1012 bit/s = 4 Tbit/s
  2. 4 × 1012 ÷ 8 = 5 × 1011 B/s = 500 GB/s
  3. At the 2x peak: 1 TB/s

Estimate 500 GB/s (anything from 150 GB/s to 1.5 TB/s is a good answer)

No data center serves this directly. It has to come from CDN edges close to viewers.

Estimate: Transcoding compute

How many CPU cores are busy transcoding on average?

You know
  • 100,000 uploads a day, 10 minutes each
  • Producing all five qualities takes about 10 CPU-minutes per minute of video (an assumption; it depends on codec and settings)
  • 1,440 minutes in a day

Show the working
  1. 100,000 × 10 minutes × 10 = 10 million CPU-minutes a day
  2. 107 ÷ 1,440 ≈ 6,900 cores busy at all times

Estimate 7 thousand (anything from 2 thousand to 20 thousand is a good answer)

A big but elastic fleet: uploads come in waves, and work can wait in a queue.

Your turn: Define the API

Write the upload endpoints so a dropped connection at 90% does not mean starting over, and the playback calls a player makes.

Compare with a model answer

Upload in three steps: start a session, send numbered chunks, then complete. A status call lists which chunks arrived so the client resumes. Playback is one metadata call to the API, then plain file requests for a manifest and segments, which a CDN can cache.

Define the API

HTTP API
POST /v1/uploads   { "filename", "size_bytes", "sha256" }
  201 { "upload_id", "chunk_size": 8000000, "chunk_urls": [signed URLs...] }
PUT  <signed chunk URL n>      (8 MB of bytes, goes straight to object storage)
  200 { "etag" }    400 checksum mismatch
GET  /v1/uploads/{id}        -> { "received": [0..111] }   resume from here
                                (the API asks storage which parts landed)
POST /v1/uploads/{id}/complete      safe to repeat: same video_id
  202 Accepted { "video_id", "status": "processing" }
  409 Conflict   chunks missing
 
GET  /v1/videos/{id}  -> { title, status, duration_s, "manifest_url" }
GET  cdn/v/{id}/master.m3u8             manifest: the qualities on offer
GET  cdn/v/{id}/720p/seg_00042.ts       one 4-second segment

The decision worth arguing is where the bytes go. Proxying 1.2 GB/s of uploads through API servers ties them up for minutes per request. Instead the API hands out signed URLs: short-lived links that let the client write one chunk directly to object storage. API servers only handle small JSON. The cost is that the API must check the result at complete, because it never saw the bytes: storage confirms every part arrived with a matching checksum, and the split job, which reads the whole file anyway, verifies the whole-file sha256. complete returns 202, not 201, because the video is not playable yet.

Your turn: Model the data

List the records for videos, uploads and processing work. Which belong in a database and which are files?

Compare with a model answer

Video rows, upload sessions and transcode jobs are small and live in a database. Originals, segments and manifests are files in object storage, addressed by a key built from the video ID. Never put video bytes in a database.

Model the data

Metadata tables
videos:   video_id BIGINT PRIMARY KEY, owner_id BIGINT, title TEXT,
          status TEXT,          -- uploading | processing | ready | failed
          duration_s INT, renditions TEXT, jobs_left INT, created_at TIMESTAMP
uploads:  upload_id, video_id, size_bytes, storage_upload_id, expires_at
          -- which parts arrived is asked of object storage, not stored here
jobs:     job_id, video_id, segment_no INT, rendition TEXT,
          status TEXT, attempts INT, lease_until TIMESTAMP
 
-- object storage keys
originals/{video_id}
v/{video_id}/master.m3u8, v/{video_id}/{rendition}/index.m3u8
v/{video_id}/{rendition}/seg_{n}.ts

The metadata is small (36.5 GB a year), relational and read far more than written, so a relational database with read replicas fits. It gives an atomic counter: each finished job decrements jobs_left, and the job that takes it to 0 writes the manifests and then sets ready. View counts are the exception: batch them through a queue, as in the URL shortener's click counting, so a viral video does not turn one row into a hot spot.

Your turn: High-level design

Draw the upload path to a playable video, and the playback path. Which boxes see video bytes, and which never do?

Compare with a model answer

Bytes flow client to object storage, object storage to transcoders and back, and object storage to CDN to viewer. The API, the metadata database and the job queue only ever see IDs and status. That split is the design.

High-level design

Bytes go through object storage and the CDN; the API and database only handle small records. Transcoding runs from a job queue.Phone, TV, browser to CDN edges (segments); Phone, TV, browser to API servers (metadata, upload); Phone, TV, browser to Object storage (PUT chunks); CDN edges to Object storage (miss); API servers to Video metadata; API servers to Object storage (join parts); API servers to Transcode jobs (enqueue), asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage (read, write); Transcoders to Video metadata (status).Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobsTranscoderssegmentsmetadata, uploadPUT chunksmissjoin partsenqueueread, writestatus

request and replyasynchronous message

Bytes go through object storage and the CDN; the API and database only handle small records. Transcoding runs from a job queue.
From upload to first playUse the arrows or ← → keys
  1. From upload to first play, step 1Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobsTranscoders
    chunks
    0 / 125

    1The phone starts an upload of a 1 GB file. The API records an upload session and returns 125 signed URLs, one per 8 MB chunk.

  2. From upload to first play, step 2Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobsTranscoders
    chunks
    112 / 125

    2The phone sends chunks straight to object storage, four at a time. API servers never touch the bytes.

  3. From upload to first play, step 3Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobsTranscoders
    chunks
    112 / 125

    3The train enters a tunnel and the connection drops. Without chunks, 900 MB would be wasted. Here, only the 4 chunks in flight are lost.

  4. From upload to first play, step 4Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobsTranscoders
    chunks
    125 / 125

    4Back online, the phone asks which chunks arrived; the API asks storage. The phone sends only the missing 13.

  5. From upload to first play, step 5Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataprocessingTranscode jobsTranscoders
    chunks
    125 / 125

    5complete: storage joins the 125 parts after checking each one's checksum. The API marks the video processing and answers 202.

  6. From upload to first play, step 6Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobs750 jobsTranscoders
    jobs left
    750

    6It enqueues a split job. A transcoder checks the whole-file checksum, cuts the video into 150 four-second pieces and enqueues 150 × 5 = 750 encode jobs.

  7. From upload to first play, step 7Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobsTranscoders
    jobs left
    212

    7Hundreds of transcoders take jobs in parallel, each reading a piece and writing one quality of it back to storage.

  8. From upload to first play, step 8Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobslease expiredTranscodersdown
    jobs left
    37

    8One transcoder dies mid-job. Its lease runs out after 2 minutes and another worker redoes that one 4-second piece; output names are fixed, so a redo just replaces the same file.

  9. From upload to first play, step 9Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadatareadyTranscode jobsTranscoders
    jobs left
    0

    9The last job takes jobs_left to 0. That worker writes the manifests to storage first, then flips the video to ready, so a ready video always has its manifests.

  10. From upload to first play, step 10Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesAPI serversObject storageVideo metadataTranscode jobsTranscoders

    10A viewer opens it. The API returns the title and manifest URL; the player fetches the manifest and segments from the nearest CDN edge.

  11. From upload to first play, step 11Phone, TV, browser to CDN edges; Phone, TV, browser to API servers; Phone, TV, browser to Object storage; CDN edges to Object storage; API servers to Video metadata; API servers to Object storage; API servers to Transcode jobs, asynchronous; Transcode jobs to Transcoders, asynchronous; Transcoders to Object storage; Transcoders to Video metadata.Phone, TV,browserCDN edgesmiss, now cachedAPI serversObject storageVideo metadataTranscode jobsTranscoders

    11The edge has never seen this video: a miss. It fetches from object storage once, then serves every nearby viewer from its own copy.

1 / 11

Deep dives

Interviewers push on four places: uploads that survive bad networks, the transcoding pipeline, how playback adapts, and the cost of delivery.

Chunked, resumable upload

A single 1 GB PUT that fails at 90% throws away 900 MB, and on a phone it will fail. Split the file into fixed 8 MB chunks: 1 GB is 125 of them. Each chunk is uploaded and checksummed on its own, so a failure costs at most the chunks in flight. Object storage services offer this as a multipart upload: parts arrive in any order, and a final call joins them into one object.

Pick the chunk size from the network. Too small (say 256 KB) and 4,000 requests each pay a round trip. Too large (say 100 MB) and a retry on a slow phone link takes minutes. 4 to 16 MB is a sound middle; I would pick 8 MB. Expire unfinished sessions after a day or two so abandoned parts do not pile up as storage nobody pays attention to.

Transcoding as a queue of jobs

Phones record in formats and bitrates that many devices cannot play and many networks cannot carry, so each upload becomes five qualities. Encoding a 10-minute video into all five takes about 100 CPU-minutes. Done on one machine, the uploader waits over an hour and a half.

How to run the transcoding work
OptionGood atBad atPick it when
One machine per videoSimplest. No splitting or joining.About 100 minutes per 10-minute video, and a crash at minute 90 starts over.Short clips only.
One job per qualityFive machines in parallel; the low qualities finish first and can go live early.Still bounded by the slowest quality: 1080p takes the largest share of the work.A quick improvement when videos are short.
One job per segment and quality Usual default750 small jobs of about 8 CPU-seconds each on average run in parallel. A failure redoes 4 seconds of video.Needs a split step, a join step, and segment cuts on keyframes so pieces play back to back.Long videos and a ready-within-minutes goal. My pick.

The arithmetic: a 4-second segment takes 4 ÷ 60 × 10 CPU-minutes = 40 CPU-seconds for the whole ladder. With 150 workers free, the 10-minute video is processed in about 40 seconds of wall time instead of 100 minutes, plus queueing. That is a simplification: splitting, uploads to storage and the final step add time, but minutes, not hours.

Make the jobs safe to repeat. Workers lease jobs and write outputs to fixed names, so a job that runs twice, after a lease expires on a slow worker, replaces the same file with an equivalent segment. The finish step must be safe to repeat too: a job decrements jobs_left only when its own row moves from running to done, so a second run cannot count twice. That is idempotency through deterministic output, and it is why the queue only needs at-least-once delivery. "Exactly once" here means exactly one effect, not exactly one run. The queue should also take priorities: a creator with millions of followers waiting should not queue behind a backlog of test uploads.

Adaptive bitrate streaming

Each quality is cut into the same 4-second segments, listed in a manifest: a small text file naming the qualities and the URL of every segment. (HLS and DASH are the two common manifest formats.) The player downloads segments one at a time and, before each one, picks the quality its measured bandwidth and buffer can sustain. Every segment boundary is a chance to switch, so a slow network means lower quality, not a frozen picture.

The player picks a quality for each segmentUse the arrows or ← → keys
  1. ladder (Mbit/s)
    00.4
    10.8
    21.5
    33
    46
    measured
    ?
    buffer
    0 s

    1Playback starts at a low quality so the first frame shows fast: 1.5 Mbit/s × 4 s = 0.75 MB for the first segment.

  2. ladder (Mbit/s)
    00.4
    10.8
    21.5
    33
    46
    measured
    12 Mbit/s
    buffer
    4 s

    2That segment arrived in 0.5 s: 6 Mbit in 0.5 s is 12 Mbit/s. The player steps up one rung to 3 Mbit/s while its buffer is still thin.

  3. ladder (Mbit/s)
    00.4
    10.8
    21.5
    33
    46
    measured
    11 Mbit/s
    buffer
    20 s

    3Bandwidth holds at about 11 Mbit/s and the buffer reaches 20 s, so it climbs to the top, keeping a margin. Each 6 Mbit/s segment is 3 MB.

  4. ladder (Mbit/s)
    00.4
    10.8
    21.5
    33
    46
    measured
    2 Mbit/s
    buffer
    16 s

    4The lift doors close: bandwidth falls to 2 Mbit/s. A 6 Mbit/s segment would take 12 s to arrive, so the player drops to 1.5 and plays from its 20-second buffer meanwhile.

  5. ladder (Mbit/s)
    00.4
    10.8
    21.5
    33
    46
    measured
    9 Mbit/s
    buffer
    18 s

    5Doors open, bandwidth recovers. The player climbs back one step at a time rather than jumping, so a short burst does not cause a stall.

  6. ladder (Mbit/s)
    00.4
    10.8
    21.5
    33
    46
    stalls
    0

    6The viewer saw lower quality for a few seconds and never a spinner. All the logic ran in the player; the servers only served files.

1 / 6

The point to make out loud: adaptation lives in the client, so the server side is plain static files. That is what lets a CDN cache them.

CDN delivery and cost

500 GB/s on average cannot come from one place, and the bill scales with bytes delivered. A CDN keeps segments on edge servers near viewers. If edges answer 95% of requests, origin storage serves 5% of 500 GB/s = 25 GB/s; at 99%, 5 GB/s. Each point of hit rate is a large saving.

Views are lopsided. Popular videos sit in every edge's cache. The long tail of rarely watched videos misses almost every time, so it is cheaper to serve from fewer, larger regional caches than to push it everywhere. A strong answer adds: pre-warm edges for a video you expect to be popular, and make segment URLs identical for every viewer so one cached copy serves them all.

Bottlenecks and failures

At 10x viewing (10 million concurrent, 5 TB/s), delivery is the whole problem: more CDN capacity, a higher hit rate and a regional cache tier to protect origin. At 10x uploads, transcoding needs about 70,000 busy cores, so make the fleet elastic and let the queue absorb bursts. The metadata database is the last thing to feel it: at 20 page loads per viewer a day, 50 million viewers make 109 ÷ 86,400 ≈ 11,600 reads a second today and about 116,000 at 10x, which read replicas plus a cache cover.

When this diesWhat happensWhat you do
A CDN edgeIts viewers' players time out on a segment.DNS or anycast steers them to the next edge; the player's buffer hides the switch.
Object storage in one regionEdge misses and new uploads fail there.Replicate finished segments to a second region and fail over; edges keep serving what they cache.
A transcoderIts job stalls.The lease expires and another worker redoes one 4-second piece.
The job queueNew videos stay in processing.Playback of existing videos is unaffected; run the queue replicated and alert on its age.
The metadata database primaryUploads cannot complete; playback pages still read replicas.Promote a replica; clients retry complete, which is safe to repeat.

What a strong answer sounds like

LevelWhat the answer does
JuniorStores videos in object storage, puts a CDN in front, and adds transcoding when asked about phones.
MidEstimates bandwidth, uses chunked uploads and a transcoding queue, and explains manifests and segments.
SeniorSeparates metadata from bytes from the start, parallelises transcoding per segment with idempotent jobs, explains why adaptation in the player makes the CDN effective, and reasons about cost through hit rate and the long tail.
Defending the transcoding designStrong answer
Interviewer

Why split videos into segments for transcoding? It adds a join step.

You

A 10-minute video needs about 100 CPU-minutes for the full ladder. On one machine that is over an hour and a half before it is watchable, and a crash late on starts over. Split into 150 segments and five qualities, it is 750 jobs averaging about 8 CPU-seconds each, so it finishes in minutes and a failure redoes 4 seconds. We need segments for playback anyway, so the join is just writing the manifest.

Follow-up questions

How would live streaming change this?

Segments are produced in real time, so transcoding must keep up second by second and the manifest is updated as segments appear. Latency becomes a requirement: shorter segments cut delay but add requests and reduce compression efficiency.

How do you count views?

The player sends a view event after a few seconds of playback, through a queue to a counter that batches updates. Counts lag by seconds and are approximate, which is fine for display.

How do you stop people downloading paid videos?

Signed segment URLs that expire stop link sharing. The edge checks the signature and leaves it out of the cache key, so one cached copy still serves everyone. Real protection needs encrypted segments and licence keys delivered separately (digital rights management), which is a component you buy, not build.

Can you save storage on old videos?

Yes: move originals to archive storage after processing, and drop rarely watched qualities for videos nobody watches, regenerating them on demand if a view arrives.

What if a creator deletes a video?

Mark it deleted in metadata so the API stops returning it, then purge it from the CDN and delete the objects in the background. Edges may serve cached segments until the purge completes, so signed URLs with short expiry help.

Self-review: video streaming0 of 12

After your attempt, tick what you really did. Be strict: "I thought about it" is not "I said it out loud".

Before drawing

Numbers and interfaces

Design and depth

Failure