Engineering Atlas › System Design Interviews

Design a video-sharing platform

18 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

Requirements

Functional:

Non-functional:

Out of scope: recommendations and search ranking, comments, live streaming, monetisation and ads, content ID and copyright matching (mentioned only as a pipeline stage).

Back-of-envelope

These are rough planning numbers. YouTube has publicly cited figures in the region of 500 hours uploaded per minute (YouTube blog, 2021) and over a billion hours watched per day; treat both as approximate.

Uploads: 500 hours per minute x 1,440 minutes = 720,000 hours of video per day. At an average length of 10 minutes that is about 4.3M uploads per day, or about 50 per second on average and maybe 150 per second at peak. Low QPS, heavy payloads.

Upload storage: a 1080p source at about 8 Mbit/s is 3.6 GB per hour. 720,000 hours x 3.6 GB is about 2.6 PB of originals per day. The transcoded ladder (for example 144p to 1080p, roughly 0.1 + 0.3 + 0.75 + 1.5 + 3 + 5 Mbit/s, about 10.7 Mbit/s summed) in two codecs is about 21 Mbit/s, or 9.6 GB per hour of content, so another 7 PB per day. Total on the order of 10 PB per day, about 3.5 EB per year and well over 10 EB in five years before erasure coding overhead. The real system stores less than this, because rarely watched videos skip some renditions or codecs and originals can move to colder storage, but the order of magnitude is exabytes.

Playback bandwidth: 1B hours per day is 3.6e12 viewer-seconds. At an average delivered bitrate of 2.5 Mbit/s, that is 9e18 bits per day, divided by 86,400 seconds gives about 1e14 bit/s, roughly 100 Tbit/s on average and perhaps 200 Tbit/s at peak.

Segment requests: with 4-second segments, 3.6e12 / 4 = 9e11 segment fetches per day, about 10M per second on average. Metadata: if there are 5B video views per day, the watch page API sees about 60,000 requests per second on average and a few times that at peak.

Transcoding compute: if encoding the full ladder costs roughly 1 to 4 CPU-hours per hour of source (much more for AV1), 720,000 hours per day needs hundreds of thousands of CPU cores busy around the clock. That is why large platforms build dedicated video transcoding hardware.

API

POST /v1/uploads                      body: { filename, size, content_type }
  -> 200 { upload_id, upload_url, chunk_size }     // resumable session
PUT  {upload_url}?offset=N            body: bytes   -> 308 { next_offset } | 200
POST /v1/videos                       body: { upload_id, title, description, visibility }
  -> 202 { video_id, status: "PROCESSING" }

GET /v1/videos/{video_id}
  -> 200 { video_id, title, channel, duration_s, status,
           manifest_url, thumbnails[], view_count }
GET {cdn}/v/{video_id}/{codec}/{rendition}/master.m3u8     // or .mpd
GET {cdn}/v/{video_id}/{codec}/{rendition}/seg_00042.m4s

Resumable uploads matter because mobile uploads of several GB fail often; the client resumes from the last acknowledged offset.

Data model

High-level design

flowchart LR
  U["Creator app"] --> UP["Upload service"]
  UP --> OBJ["Blob storage"]
  UP --> MQ["Job queue"]
  MQ --> SPL["Splitter"]
  SPL --> TX["Transcode workers"]
  TX --> OBJ
  TX --> MAN["Manifest builder"]
  MAN --> META["Metadata DB"]
  V["Viewer player"] --> API["Watch API"]
  API --> META
  V --> EDGE["Edge cache"]
  EDGE --> MID["Regional cache"]
  MID --> OBJ

Upload path: the client opens a resumable session and streams chunks directly to blob storage through the upload service. When the last chunk lands, the service records the video in PROCESSING and enqueues a job. A splitter cuts the source at keyframes into chunks of a few seconds to a minute, then creates one transcode task per chunk per rendition. Workers pull tasks with leases, encode, and write segments. When all tasks for a rendition finish, the manifest builder writes the playlist, and the video becomes playable as soon as a minimum set (for example 360p and 720p in H.264) is ready. Higher resolutions and newer codecs such as VP9 or AV1 follow later, often only for videos that get watched.

Playback path: the player fetches metadata and the manifest URL from the watch API (one cached database read), then requests the master manifest and segments from the nearest edge cache, chosen by DNS or anycast steering. The edge serves hits from memory or SSD; misses go to a regional cache and then to origin storage. The player measures download time per segment and picks the next rendition.

Deep dives

Parallel, resumable transcoding

Encoding a two-hour film serially takes hours. Splitting at closed group-of-pictures boundaries lets thousands of workers encode chunks in parallel and then concatenate, so wall-clock time drops to minutes. The options for orchestration are a simple queue of independent tasks, or a workflow engine that models the job graph (split, encode chunks per profile, verify, stitch, package, publish). A DAG engine with per-task leases is the choice: a worker that dies stops renewing its lease, the task returns to the queue, and since outputs are written to deterministic paths, a retried task just overwrites an identical file. Prioritise short videos and creators with large audiences in the queue so the median video is playable in a minute or two. Encode a cheap rendition first so users get a quick "ready" signal.

Adaptive bitrate on the client

The player has a ladder of renditions and a buffer of downloaded video. Two signals drive selection. Throughput-based ABR estimates bandwidth from recent segment downloads (a harmonic mean is common because it discounts spikes) and picks the highest rendition below a safety fraction of that estimate. Buffer-based ABR (BBA, BOLA) chooses quality from buffer occupancy, which avoids oscillation caused by noisy estimates (BOLA). Production players blend the two; the reference DASH player, dash.js, runs a throughput rule and a BOLA rule and switches between them by buffer level (dash.js). The usual policy is to drop quality quickly when the buffer is low or throughput falls, raise it one step at a time and only when the buffer is healthy. Segment length is part of this: 2 to 6 seconds balances fast switching and startup against per-request overhead and encoding efficiency.

Delivering 100+ Tbit/s

No single data centre can serve this. The answer is a tiered cache: edge nodes in ISP networks and internet exchanges (Google Global Cache is the public example, Netflix Open Connect another), regional mid-tier caches, and origin. Because segments are immutable and keyed by content, cache invalidation is not a problem; only manifests for edits or takedowns need short TTLs. Popularity is heavy-tailed, so a modest edge capacity can serve a very large share of bytes, while the long tail costs origin egress. Pre-position content predicted to trend (a new release from a large channel) to edges before demand arrives. Use request coalescing at each tier so a thousand simultaneous misses for the same new segment produce one upstream fetch.

Variant: a subscription catalogue service (Netflix-style)

Change the product and most of the design inverts. A licensed catalogue is small and known in advance: perhaps 50,000 hours of content, a few petabytes once every codec, HDR and audio variant is encoded. It grows by tens of content hours a day, not hundreds of thousands. Every title is watched many times, so the expensive step moves to the front and the delivery path gets simpler.

Encode once, with great care. With no long tail of unwatched uploads, spending hours of compute per title is repaid on every stream. Replace one fixed bitrate ladder with per-title or per-shot ladders chosen by a perceptual quality score such as VMAF, with shot boundaries as the parallel split points. Reported savings are around half the bitrate at equal quality for 4K, and newer codecs such as AV1 are added as extra streams (Netflix). Here the upload-site habit of reserving expensive encodes for popular videos does not apply.

Push, don't pull. Because tomorrow's demand per region is predictable, a planner copies files to cache appliances inside ISPs during off-peak fill windows, ranking files by forecast demand per byte. Files already on disk get a bonus so that nothing is evicted and refetched the next day, and deletions are delayed so open streams are not cut. A pull-through tier waits for the first viewer to miss; this one rarely sees a miss (Netflix and Fill).

Control plane and data plane. The cloud decides identity, entitlement, DRM and which cache URLs to return, ranked by the client's network, health and inventory. The appliances only move bytes. Playback survives the control plane failing, because a running stream needs only the cache and its licence.

Different state. Plan rules limit simultaneous streams, so a play request claims a lease that heartbeats extend and expiry frees; a counter would leak when a TV loses power. Resume positions are written about once a minute per playing device, which at tens of millions of concurrent streams makes bookmarks the write-heavy path, not play starts. Conflicts resolve by device event time, since an offline phone may report an old position late.

Different requirements. Start within about two seconds on TVs, scheduled worldwide release at a set time, and graceful degradation: the worst case is a generic home page, not a black screen. Prioritised load shedding keeps play requests alive while personalisation is dropped first.

Down the stack

Layer 1The contract

A segment URL always returns the same bytes, forever, or a 404 after a takedown. The manifest lists renditions and segment URLs and may change only by adding renditions. The edge promises low time to first byte for cached objects (single-digit milliseconds within the ISP) and correct byte-range support. The upload API promises that an acknowledged chunk is durable, and that a video in READY has at least the baseline renditions complete.

Layer 2The mechanism

Segments are fragmented MP4 (moof plus mdat boxes, CMAF) or MPEG-TS, each beginning at a keyframe so the player can switch renditions at any segment boundary. An HLS master playlist lists #EXT-X-STREAM-INF entries with BANDWIDTH and RESOLUTION; each media playlist lists #EXTINF durations and segment URIs (RFC 8216). RFC 8216 dates from 2017; its second edition, draft-pantos-hls-rfc8216bis, is in the RFC Editor queue and would obsolete it, so check the draft for newer tags (IETF datatracker). The player appends segment bytes to a Media Source Extensions SourceBuffer and runs its ABR function before each request. Caches key on the full URL, use LRU-like eviction with admission filters (do not cache a one-hit object), and coalesce concurrent misses. Akamai found 74% of cached objects on a typical cluster were requested only once in two days, and caching on the second hit cut disk writes by 44% (Maggs and Sitaraman).

Layer 3The machine

Edge servers are built around zero-copy file serving: sendfile from page cache or NVMe straight to the socket, with TLS offloaded into the kernel (kTLS) or the NIC so bytes are not copied through user space. A single server with several 100 Gbit/s NICs can push hundreds of Gbit/s when the working set is in RAM and on NVMe. A 4-second 1080p segment at 5 Mbit/s is 2.5 MB, which is about 1,700 TCP segments of 1,460 bytes; on a fresh connection, slow start needs several round trips to open the congestion window, which is why players reuse connections (HTTP/2 or QUIC) and why startup uses a low rendition. Transcoding is SIMD-heavy and memory bandwidth bound; dedicated video encoding ASICs (YouTube's Argos VCU) exist because general CPUs spend too much power per encoded hour. YouTube reports up to 20 to 33 times better compute efficiency than its optimised software encoders, and notes VP9 needs about 5 times the compute of H.264 to encode (YouTube blog).

Why it's built this way

  • Bandwidth is the dominant cost, so bytes must be served from as close to the viewer as possible.
  • Caching is only easy for immutable objects, so video is chopped into content-addressed segments.
  • Network conditions differ per viewer and change every second, so the client chooses quality, not the server.
  • Encoding is slow and parallelisable at keyframes, so split, fan out, and stitch.
  • Most videos are rarely watched, so expensive encodings and edge placement are reserved for popular ones.

Failure modes

In the wild

Trade-offs

OptionStrengthsWeaknessesChoose when
Short segments (2 s)Fast startup and switchingMore requests, slightly worse compressionLive or highly variable networks
Long segments (6 to 10 s)Fewer requests, better compressionSlow adaptationStable networks, VOD on TV
Throughput-based ABRReacts quickly to bandwidthNoisy estimates cause flappingStartup, fast-changing networks
Buffer-based ABRStable quality, fewer stallsSlow to ramp upSteady state playback
Encode all codecs upfrontBest quality for every viewHuge compute for unwatched videosPremium or popular content
Encode on demand by popularitySaves compute and storageFirst viewers get lower efficiencyLong-tail user uploads

Leadership lens

Check yourself

  1. Where does the roughly 100 Tbit/s playback figure come from, and what does it tell you about where to spend design effort?

    Show answer

    1B hours a day is 3.6e12 viewer-seconds, and at 2.5 Mbit/s that is about 9e18 bits a day, or about 1e14 bit/s. Egress is the dominant cost, so the tiered CDN with caches inside ISP networks is the real architecture, while origin storage and databases are small by comparison.

  2. Why are videos cut into immutable segments behind a manifest rather than served as single files?

    Show answer

    A segment URL always returns the same bytes, so segments can be cached everywhere with no invalidation problem, and only manifests need short TTLs. Each segment starts at a keyframe, which also lets the player switch renditions at any segment boundary.

  3. A transcode worker dies halfway through a chunk. Why does that not corrupt the video or require special recovery?

    Show answer

    Workers hold per-task leases in a DAG engine, so a dead worker stops renewing, the task returns to the queue, and another worker picks it up. Outputs go to deterministic paths, so a retried task just overwrites an identical file.

  4. When would you encode renditions on demand by popularity instead of encoding every codec upfront?

    Show answer

    For long-tail user uploads, since most videos are rarely watched and encoding all codecs upfront spends huge compute on unwatched videos. The cost is that the first viewers get lower efficiency, so full encoding suits premium or popular content where the egress savings pay off.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →