Guide contents

Lesson 106 of 129

Design YouTube

A video sharing service lets users upload videos and lets other users stream them on any device and network. The hard parts are the size of the data (video dominates storage and bandwidth), the asynchronous processing needed to convert uploads into playable formats, and delivering video with low start-up delay and no stalls on variable networks.

Requirements

Functional

  • Upload videos (files up to several GB).
  • Stream videos on web, mobile, and TV clients.
  • Search and browse by title, channel, and tags.
  • Record views, likes, and comments (only view counts are covered in depth).
  • Video metadata: title, description, thumbnail, channel.

Non-functional

  • Fast playback start (roughly a second or two) and few rebuffering events.
  • High availability for playback. Upload and processing may tolerate delay.
  • Durability: an accepted upload must never be lost.
  • Scalability: read-heavy, with far more viewers than uploaders.
  • Cost efficiency: storage and egress bandwidth are the dominant costs.
  • Eventual consistency is acceptable for view counts, search index, and recommendations.

Out of scope: recommendation ranking, live streaming, copyright detection, ads.

Estimation

Assumptions: 500 million daily active users (DAU), 5 videos watched per user per day, and 1 million new videos uploaded per day.

  • Video reads: 500M x 5 = 2.5 billion views/day. 2.5 x 10^9 / 86,400 s is about 29,000 views/s on average. Peak at 3x is about 90,000 view starts/s.
  • Uploads: 1M/day is about 12 uploads/s.
  • Raw upload size: average 500 MB per video gives 1M x 500 MB = 500 TB/day of raw uploads.
  • Stored size after transcoding: assume the full ladder of renditions (for example 240p to 4K) plus replication totals about 2x the raw upload size. That gives 1 PB/day of stored data, about 365 PB/year. The factor is an assumption, not a measured value.
  • Egress bandwidth: at an average stream bitrate of 3 Mbps and an average of 5 minutes watched per view, each view transfers 3 Mbps x 300 s = 900 Mb, about 112 MB. Daily egress is 2.5 x 10^9 x 112 MB, about 280 PB/day, or about 26 Tbps averaged over the day. No origin can serve this, so a CDN must absorb almost all of it.
  • Metadata: 1M videos/day x 1 KB = 1 GB/day. Trivial compared with video bytes.

Conclusions: video bytes belong in blob storage plus CDN, metadata in a database, and CDN cost is the main design constraint.

What each number implies:

  • Concurrency: 29,000 view starts/s x 300 s is about 8.7 million concurrent viewers on average and about 26 million at peak. With 4-second segments, that is about 2 million segment requests per second at the edge on average. This is a CDN workload; origin never sees it.
  • Origin exposure equals the miss rate: at 26 Tbps average, a 95% edge hit ratio leaves 1.3 Tbps to fetch from origin or shield; 99% leaves 260 Gbps. Each point of hit ratio is worth tens of Gbps, so cache design and the origin shield are first-order decisions.
  • Transcoding compute (assumption): if the full ladder costs about 10 core-minutes per video-minute and the average video is 10 minutes, then 1M uploads/day needs 100 million core-minutes per day, about 70,000 cores continuously. This justifies preemptible workers and cheaper ladders for the long tail.
  • 12 uploads/s but 500 TB/day: the upload path is bandwidth-bound, not request-bound; uploads go directly to blob storage, not through API servers.

API design

Code
POST /v1/videos                      -> { videoId, uploadUrl }     (create upload session)
PUT  <uploadUrl>  (Content-Range)    -> 308 / 200                  (resumable chunk upload)
POST /v1/videos/{id}/complete        -> 202 Accepted               (start processing)
GET  /v1/videos/{id}                 -> metadata + manifest URL
GET  /v1/videos/{id}/manifest.mpd    -> streaming manifest (DASH) or .m3u8 (HLS)
GET  /v1/search?q=...&pageToken=...  -> list of video summaries
POST /v1/videos/{id}/views           -> 204 (view event, batched by client)

The upload URL points directly at blob storage (a pre-signed URL), so video bytes do not pass through application servers. Chunk ranges allow resuming after a network failure.

Data model

Data Store Reason
Video files (original, renditions, segments, thumbnails) Object storage (object and blob storage) Large immutable objects, cheap, durable via replication or erasure coding
Video metadata (id, owner, title, status, renditions) Sharded relational DB or wide-column store, keyed by video_id Small records, point lookups, high read volume with a cache in front
Channel and user data Relational DB Structured, low write rate
View counters Stream aggregation into a key-value store Very high write rate, approximate is fine
Search index Inverted index (Elasticsearch style, see inverted index) Text queries

Metadata row: video_id, channel_id, title, description, duration, status (uploading, processing, ready, failed), created_at, rendition_list, manifest_path. Partitioning by video_id hash spreads load (see sharding and partitioning).

High-level design

graph TD
    C["Client"] --> API["API servers"]
    C -->|"chunk upload (pre-signed URL)"| RAW["Raw video storage"]
    API --> META[("Metadata DB + cache")]
    API -->|"upload complete event"| Q["Message queue"]
    Q --> SPLIT["Splitter / DAG scheduler"]
    SPLIT --> W["Transcoding workers"]
    W --> OUT["Processed video storage"]
    W --> THUMB["Thumbnails"]
    W -->|"status: ready"| META
    OUT --> CDN["CDN edge caches"]
    CDN --> C
    API --> SEARCH["Search index"]
    C -->|"view events"| VIEW["View count pipeline"]

Components:

  • API servers. Stateless. Create upload sessions, serve metadata, return manifest URLs.
  • Raw video storage. Receives the original upload. Kept until processing succeeds, then usually archived or deleted.
  • Message queue. Decouples upload completion from processing (see message queues vs pub/sub).
  • Transcoding pipeline. Converts the original into a set of renditions and streaming segments.
  • Processed video storage and CDN. Origin for segments. The CDN serves nearly all playback traffic.
  • Metadata DB and cache. Tracks status and the location of manifests.

Upload flow

sequenceDiagram
    participant C as Client
    participant A as API server
    participant S as Object storage
    participant Q as Queue
    participant T as Transcoder
    C->>A: POST /videos (title, size)
    A-->>C: videoId, pre-signed upload URL
    loop each chunk
        C->>S: PUT chunk with Content-Range
        S-->>C: bytes received
    end
    C->>A: POST /videos/{id}/complete
    A->>S: verify size and checksum
    A->>Q: enqueue process videoId
    A-->>C: 202 Accepted
    Q->>T: process videoId
    T->>S: write renditions and manifest
    T->>A: mark video ready

Playback flow

The client fetches metadata, then the manifest, a text file listing the available renditions and the URLs of their segments. The player downloads segments from the nearest CDN edge and switches renditions as bandwidth changes.

Deep dive

1. Transcoding pipeline

Transcoding converts a video into multiple resolutions, bitrates, and codecs. Reasons: clients differ in supported formats, and one bitrate cannot suit all networks.

Process:

  1. Split the source into groups of pictures (GOPs), each starting at a keyframe, typically a few seconds long.
  2. Each chunk is independent, so chunks are transcoded in parallel across workers.
  3. Each chunk is encoded into every rendition in the ladder (for example 240p, 360p, 480p, 720p, 1080p, 4K).
  4. Segments are stitched into the streaming layout, and the manifest is written.
  5. Audio is encoded separately. Thumbnails are extracted at intervals.

Pipelines differ per video (watermarking, different codecs), so the work is modeled as a directed acyclic graph (DAG) of tasks. A scheduler enqueues ready tasks, workers pull them, and failed tasks retry. Tasks must be idempotent, because at-least-once queue delivery can run one twice (see delivery semantics and idempotency). Output paths are deterministic, so a retry overwrites the same object.

Alternatives: encode only popular videos in expensive codecs and encode long-tail videos in a cheap ladder, since encoding cost is only worth paying when views will amortize it.

2. Adaptive bitrate streaming

Adaptive bitrate (ABR) streaming splits each rendition into short segments (commonly 2 to 10 seconds). The client measures throughput and buffer level and picks the rendition for the next segment. Protocols are HTTP Live Streaming (HLS) and MPEG-DASH. Both use plain HTTP, so standard web servers and CDNs work without special streaming servers, and segments cache well because each is an immutable object with a stable URL.

Trade-offs:

  • Shorter segments react faster to bandwidth changes but add request overhead and slightly reduce compression efficiency.
  • More renditions improve fit but multiply storage and encoding cost.
  • Progressive download of a single file avoids the manifest but cannot adapt.

3. CDN strategy and cost

Egress is the largest cost. Measures:

  • Serve all segments through a CDN. Popular videos stay in edge caches. Video popularity follows a heavy-tailed distribution, so a small fraction of videos account for most views and hit in edge caches.
  • Use a pull CDN with an origin shield layer, so a cache miss in many edges produces one origin fetch.
  • Pre-warm (push) segments of a video expected to be popular, such as a release from a large channel.
  • Long-tail videos are served from regional caches or the origin. Do not cache every rendition of rarely watched videos at every edge.
  • Serve from edges close to viewers to reduce start-up latency, and place content in caches inside ISPs where partnerships exist.

Concrete thresholds: use immutable segments with a very long cache TTL (up to a year) because a segment URL never changes content; keep the manifest TTL short only for content that changes (live, or after edits). Target an edge hit ratio above 95% for the head; for the long tail, accept a lower hit ratio and serve from regional caches to avoid filling every edge with content watched twice a month. Alternative to a purely pull-through model: pre-push only the first few segments (for example 10 to 20 seconds) of new videos from popular channels, so start-up is fast while the rest is pulled on demand.

Metadata store: shard the video table by video_id hash; a separate index keyed by channel_id serves channel pages (a secondary table, since it cannot be answered from a video_id shard key). Cache metadata by video_id with a TTL of 1 to 5 minutes plus invalidation on edit; replicate entries for the hottest videos across cache nodes to avoid one node serving a viral video's metadata.

4. Resumable, safe uploads

Large files on unreliable networks require chunked uploads. The server tracks which byte ranges have been received, and the client queries the offset after a failure and continues. Chunks can be uploaded in parallel and assembled by the storage service (multipart upload). On the complete call the API server verifies the total size and a checksum before publishing the completion event to the queue. Unfinished sessions expire and their chunks are deleted by a lifecycle rule.

Chunk size (for example 8 MB) trades retry cost against request overhead: a 500 MB file is about 63 chunks, so a failed chunk costs 8 MB, not 500 MB.

5. View counts

A single row counter updated per view would be a hot spot. Clients or edge servers send view events to a log (Kafka), an aggregator sums counts in time windows and updates the counter store in batches. Counts shown to users are eventually consistent, and fraud filtering runs offline before counts are finalized.

View count numbers: partition the event log by video_id, aggregate in 10 to 60 second windows, then write one increment per video per window, which turns 90,000 events/s into perhaps tens of thousands of batched increments. A viral video still concentrates on one partition, so pre-aggregate at the edge or add a random sub-key per event and merge partial sums downstream. Displayed counts may lag by minutes; fraud filtering finalizes them later.

Failure modes

  • Transcode worker crashes. The task becomes visible again after the queue's visibility timeout and reruns idempotently. After about 3 failed attempts a task goes to a dead-letter queue and the video is marked failed with a reason.
  • Transcode backlog. Publish the low renditions first (for example 240p to 480p) so the video is watchable in minutes, and add higher renditions when ready.
  • CDN provider or region degraded. Fail over to a second CDN by changing manifest hostnames; origin capacity is far smaller than CDN capacity, so ABR should be nudged to lower bitrates during failover to keep total bandwidth within limits.
  • Cold-cache stampede for a new viral video. Edge request coalescing and the origin shield turn thousands of simultaneous misses into one origin read per segment.
  • Metadata database shard down. Cached metadata and manifests keep popular videos playable; writes (uploads, edits) for that shard fail. Shed likes and comments before playback.
  • At 10x scale (about 260 Tbps): commercial CDN capacity and cost stop working, so caches embedded in ISPs become necessary; storage reaches about 10 PB/day, so codec efficiency (newer codecs cost more encoding compute but save bytes), tiering, and encoding only the long tail on demand become mandatory; transcoding would need about 700,000 cores if done up front for everything.

Bottlenecks and scaling

  • Egress bandwidth. Addressed by CDN, ABR, efficient codecs, and caching policy.
  • Transcoding compute. Scale workers with queue depth. Use preemptible capacity because tasks are retryable. Prioritize by channel size so popular uploads finish first.
  • Metadata reads. Cache video metadata by video_id. Popular videos are hot keys, so add replicated cache entries.
  • Hot videos. A viral video concentrates requests on a few segment objects. CDN edge caches absorb these, and request coalescing at the edge prevents a stampede on the origin.
  • Storage growth. Move old, rarely watched videos to cheaper tiers, delete raw originals after successful processing, and use erasure coding for cold data.
  • Failures. A failed transcode is retried, then marked failed with a user-visible status. Storage replicated across zones protects durability. Playback survives origin failure for cached content.

Trade-offs and follow-up questions

  • Encode everything up front versus encode on demand: up-front gives consistent start-up latency, on-demand saves compute for unwatched videos.
  • More renditions and codecs versus storage cost.
  • Segment length: fast adaptation versus overhead.
  • How would live streaming change the design? Segments are produced continuously, latency targets are seconds, and there is no time to transcode ahead of the viewer.
  • How to detect duplicate uploads? Fingerprint content (a hash of the file, and perceptual fingerprints for re-encodes) before transcoding.
  • How to keep private videos private? Signed, expiring URLs for segments and authorization at the manifest request.
  • How do you get playback to start in 1 to 2 seconds? Start at a middle rendition with a short first segment, prefetch the manifest with the metadata, keep connections warm to a nearby edge, and ramp quality up as the buffer fills.
  • What if a video is deleted or blocked for copyright? Mark it in metadata immediately, refuse new manifests, expire signed URLs (minutes), and purge the CDN; already-cached segments become unreachable once URLs stop validating.
  • How do you resume watching where the user left off? Store a per-user, per-video position, written every 10 to 30 seconds from the client, in a key-value store.
  • What if popular videos need stronger consistency for edits, such as changing a title? Update the database, then invalidate the cache key; eventual consistency for a few seconds is acceptable.
  • How to support regional content restrictions? Enforce at manifest generation and at the CDN with geo-based rules.