SYSTEM DESIGN · DESIGN PROBLEMS · MEDIA AND STORAGE · MEDIUM · 16 OF 41

Design YouTube: Resumable Uploads, a Transcoding DAG, Adaptive Streaming and View Counts

How to design a long-form video platform like YouTube: requirements, upload and egress math, resumable upload sessions, a transcoding pipeline built as a DAG of parallel jobs, adaptive bitrate streaming with HLS or DASH, CDN tiers and origin shielding for the long tail, counting billions of views with dedupe and batching, metadata storage, and the trade-offs.

Reported atGoogle

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
1 / 18

Press play to hear the narration, or step through at your own pace.

Notes

Listen

Requirements. Creators upload videos, possibly many hours long and many gigabytes. We process each into formats every device can play. Viewers watch on any screen and any network, and can jump to any point. The watch page shows title, channel, view count and likes. Recommendations, live streaming and comments are out of scope today; mention that the recommendation design looks like the TikTok funnel.

Qualities. Playback should start within a second or two and rarely stall. Uploads must survive flaky connections; nobody wants to restart a ten-gigabyte upload at ninety percent. A new video should be playable within minutes, at least in common resolutions. Originals are never lost. View counts must be approximately right and resistant to inflation. And cost matters, because bandwidth will be the biggest bill by far.

Functional requirements
  • in scope:
  • • upload videos up to ~12 h
  • • process into many formats
  • • watch on any device, any
  • network, seek anywhere
  • • video page: title, channel,
  • view count, likes
  • out of scope:
  • • recommendations in depth,
  • live streaming, comments
Non-functional requirements
  • playback starts in ~1-2 s,
  • rare rebuffering
  • uploads survive bad networks
  • video ready in minutes, not
  • hours (for common formats)
  • never lose an original
  • views: approximately right,
  • never wildly inflated
  • cost matters: egress is huge

Ask scale

Uploads: YouTube has publicly said more than five hundred hours of video arrive every minute. That's about seven hundred twenty thousand hours a day. At around two gigabytes per hour for an HD original, that's more than a petabyte of originals daily, and the renditions roughly double it. That's on the order of an exabyte a year, so storage must be tiered: hot for popular videos, cold for the huge tail nobody watches.

Viewing: eight hundred million daily viewers watching forty minutes each at an average of two and a half megabits a second. Work it through and that's about six hundred petabytes a day, roughly fifty-five terabits per second on average. No data center serves that. The design has to make the CDN do almost all the work, with the origin seeing only a small percentage of requests.

Uploads and storage
upload rate   ~500 hours/min
  x 1,440 min = 720K hours/day
original      ~2 GB per hour
  = ~1.4 PB/day of originals
renditions    ~1x original more
  = ~3 PB/day total
  x 365       = ~1 EB/year

-> tiered storage: hot, warm,
   cold for old, unwatched
Viewing and bandwidth
DAU                 800M
watch time/user     40 min/day
  = 1.9 trillion s of video
avg bitrate         2.5 Mbps
  = 4.8e18 bits/day
  = ~600 PB/day
  / 86,400 s = ~55 Tbps avg

video starts: 4B/day = 46K/s
-> CDN; origin sees a few %

Define API

The API. An upload starts by creating a session, which returns a session URL. The client sends the file in pieces with byte ranges. If the connection drops, it asks the session for the last byte received and continues from there. The watch page returns metadata and a manifest URL. The player fetches the manifest and segments from the CDN. And the player sends playback events, which drive view counts and quality metrics.

Core API
POST /v1/uploads
  { size, title } -> { sessionUrl }
PUT  sessionUrl
  Content-Range: bytes 0-8M/9.8G
GET  sessionUrl -> next offset
GET  /v1/videos/{id}
  -> { title, views, manifest }
GET  {cdn}/v/{id}/master.m3u8
POST /v1/playback/events
  { videoId, pos, state }

Data model

Data model. A video row has its channel, title, duration, visibility and a processing status. Each rendition row describes a codec, resolution and bitrate and points at its manifest. Segments are files in object storage with predictable keys. Counts live separately because they change constantly. Metadata is small enough for sharded SQL, and it's read far more than written, so a cache in front absorbs most reads.

Records
video   (videoId, channelId,
         title, duration, status,
         visibility, createdAt)
rendition (videoId, codec, height,
         bitrate, manifestKey)
segment  key: v/{id}/{rend}/{n}.m4s
channel (channelId, ownerId)
counts  videoId -> views, likes

metadata: sharded SQL or
  wide-column, heavily cached

High-level design

The upload path. The creator's file streams into an upload session and lands as the original in raw storage. When the upload completes, a pipeline planner inspects the file and builds a graph of jobs: split, encode each segment in each rendition, make thumbnails, extract audio for captions. Workers run those jobs in parallel and write the results to video storage, and the planner updates the video's status as stages finish.

The watch path. The viewer's app fetches the video page from the watch API, which reads cached metadata. Then the player pulls the manifest and segments from a nearby CDN edge. On an edge miss, the request goes to a regional origin shield, and only if that misses too does it reach video storage. Meanwhile the player sends playback events into a stream, which feeds the view counter and quality dashboards.

Deep dive: processing

Deep dive one: processing. Encoding a two-hour video in one piece would take hours. Instead, split it at keyframes into short chunks, and encode each chunk for each rendition as an independent job. Hundreds of workers finish in minutes. The planner treats this as a DAG: split before encode, encode before stitching the manifests. Publish as soon as a couple of common resolutions are ready, and let higher resolutions and more efficient codecs arrive later.

Make the pipeline reliable. Jobs sit on a durable queue. Each worker takes a lease on a job; if it crashes, the lease expires and another worker retries. Every job writes to a key determined by video, rendition and segment, so a retry simply overwrites the same output. A corrupt file fails after a few attempts and is marked failed rather than retried forever. Because jobs are small and retryable, you can run them on cheap preemptible machines.

Transcoding DAG
  • 1 inspect: codec, length, fps
  • 2 split at keyframes, ~10 s
  • 3 per segment x rendition:
  • 144p ... 1080p, 4K if source
  • H.264 + VP9 or AV1
  • 4 thumbnails, audio tracks
  • 5 stitch manifests (HLS, DASH)
  • 6 safety + copyright checks
  • 360p and 720p first -> publish
  • rest arrive later
Making it reliable
  • jobs on a durable queue
  • each job idempotent: output
  • key = video + rend + segment
  • worker dies: lease expires,
  • another worker retries
  • poison file: fail after N
  • tries, mark video failed
  • priority lanes: popular
  • creators, short videos first
  • spot or preemptible machines

Deep dive: streaming

Deep dive two: streaming. The master manifest lists every rendition. Each one is a sequence of segments a few seconds long. The player measures how fast segments arrive and how full its buffer is, and picks the bitrate for the next segment. When your train enters a tunnel, it drops to four eighty p instead of stalling. Seeking just means requesting segment number N. Start at a lower rung for a quick first frame and climb as the buffer fills.

The CDN has to handle a long tail. A small fraction of videos gets most views, and those sit in every edge cache. But billions of rarely watched videos can't be everywhere, so a regional origin shield catches misses from many edges, and only its misses reach storage. Big platforms also place cache servers inside internet providers' networks to cut transit cost. Before a huge premiere, prewarm the edges.

Adaptive bitrate
  • master manifest lists all
  • renditions and bitrates
  • each segment: 2-6 s of video
  • player measures throughput
  • and buffer level, then picks
  • the next segment's rung
  • seek = jump to segment N
  • start low for a fast start,
  • climb as the buffer grows
CDN for a long tail
  • head: few videos, most views
  • -> cached at every edge
  • tail: billions of videos,
  • rare views
  • -> regional shield caches,
  • then storage
  • edge boxes inside ISPs cut
  • transit cost
  • cache key ignores user
  • prewarm a big premiere

Deep dive: views

Deep dive three: view counts. A view isn't a page load; it's a playback event that passes rules, such as watching past a threshold. Events flow through a stream partitioned by video. A filter drops bots and repeats from the same viewer within a window. An aggregator sums per minute and writes one update per video, instead of millions of increments to one hot row. The displayed count is cached and may lag, and because the raw log is kept, counts can be audited and recomputed.

Counting views
  • event: played past ~30 s
  • (or most of a short clip)
  • stream partitioned by videoId
  • filter: bots, repeat views by
  • one viewer in a window
  • aggregate per minute, write
  • totals: one row update per
  • video per minute
  • display: cached, may lag
  • audit: recount from the log

Trade-offs

Failures and monitoring. If encoding backs up, publish low resolutions first and autoscale workers on queue depth. If a CDN edge fails, traffic shifts to the next one through DNS or anycast. If a shield gets hammered by misses for one popular segment, coalesce requests so only one goes to storage. The key metrics are viewer-side: time to first frame and rebuffering ratio, plus encode lag and the bandwidth bill.

Trade-offs. Every extra rendition and codec costs storage and compute, but saves bandwidth and improves playback. Efficient codecs like AV1 are expensive to encode, so spend that only on videos that will be watched a lot. Approximate, delayed view counts trade freshness for scale and fraud resistance. At ten times the scale, push more caches into provider networks and move old, unwatched videos to cold storage, keeping only a few renditions.

Failures and monitoring
  • encode backlog: publish low
  • resolutions first, scale
  • workers on queue depth
  • CDN edge down: DNS or anycast
  • to the next edge
  • shield overloaded: request
  • coalescing for one segment
  • metadata DB: cache, replicas
  • watch: start time, rebuffer
  • ratio, encode lag, egress $
Trade-offs to name
  • more renditions + codecs:
  • storage and compute for
  • smoother, cheaper playback
  • AV1: costly encode, saves
  • bytes, so only for hot videos
  • approximate counts: lag for
  • scale and fraud resistance
  • 10x: more ISP caches, cold
  • tier for the tail

Say it like this

The summary. Uploads are resumable sessions into object storage. A pipeline builds a DAG of small idempotent jobs, splitting at keyframes and encoding segments in parallel, so common resolutions publish in minutes. Playback uses HLS or DASH, with the player adapting bitrate per segment, served from CDN edges backed by regional shields. Views are playback events that are filtered and aggregated per minute. Metadata is small, relational and cached.

The 60-second answer
  • resumable upload sessions
  • -> raw object storage
  • DAG: split at keyframes,
  • encode segments in parallel,
  • publish common sizes first
  • HLS or DASH, ABR in player
  • CDN edge -> shield -> storage
  • views: events -> filter ->
  • per-minute aggregates
  • metadata: cached SQL

Interview questions

How do you make large uploads reliable?

Use a resumable upload session: the client sends byte ranges, and after a failure asks the server for the last byte it received and continues from there. The original is only marked complete once every byte is durably stored.

How do you transcode a long video quickly?

Split it at keyframes into short chunks and encode each chunk for each rendition as an independent job on many workers, then stitch the outputs into manifests. A planner runs these as a DAG and publishes once common resolutions are done.

What is adaptive bitrate streaming?

The video is encoded at several bitrates and cut into short segments listed in an HLS or DASH manifest. The player measures throughput and buffer level and picks the bitrate of each next segment, so quality drops instead of the video stalling.

How do you serve both viral videos and the long tail efficiently?

Popular videos are cached at every CDN edge, while rarely watched ones rely on regional origin shields that aggregate misses from many edges before touching storage. Old, unwatched videos move to cheaper storage tiers.

How would you count views at this scale?

Treat views as playback events that pass a watch threshold, stream them partitioned by video, filter bots and repeats, and aggregate counts per minute before writing totals. Keep the raw events so counts can be audited or recomputed.

What happens if an encode worker crashes mid-job?

The job’s lease expires and another worker picks it up. Outputs are written to deterministic keys, so a retried job overwrites the same segment rather than creating duplicates.

Where does most of the cost come from?

Egress bandwidth, which is orders of magnitude larger than upload volume. You reduce it with high CDN hit rates, caches inside ISP networks and more efficient codecs for the most-watched videos.

Why not encode every video in every codec at upload time?

Most videos get few views, so expensive encodes like AV1 would cost more compute than they save in bandwidth. Encode common formats first and add efficient codecs only for videos that become popular.