Back to Blog
System DesignInterview TipsDistributed SystemsFAANG

Design YouTube — System Design Interview Walkthrough

Amit Singh

Amit Singh

Author

June 25, 2026
19 min read

The key insight for designing YouTube is that it's two systems wearing one logo: a heavy video pipeline (upload → transcode → store → CDN) and a lightweight metadata service. Keep them separate, serve the bytes from a CDN (never your servers), and make transcoding asynchronous, and the design falls into place. This applies the standard system design framework to a storage- and bandwidth-bound problem.

The mistake candidates make is treating video like normal data. It isn't — a single file is gigabytes, viewers are global, and reads dwarf writes. So the design is dominated by storage, transcoding, and delivery, not by the database.

1. Requirements

Functional: upload a video; watch a video (smooth playback on any device/network); basic metadata (title, description); view counts; search by title. Out of scope (state it): recommendations, comments, monetization, live streaming.

Non-functional, quantified: extremely read-heavy (views ≫ uploads); global playback that starts in under ~2 seconds and never stalls; high durability (an uploaded video must never be lost); and elastic capacity to absorb transcoding spikes.

2. Back-of-the-envelope: find the cost centers

Run the estimate all the way to a conclusion about where the bytes — and the dollars — go. Assume ~500k uploads/day and ~5B views/day (illustrative, but kept self-consistent).

  • Rates: 500k / 86,400 ≈ ~6 uploads/sec; 5B / 86,400 ≈ ~58,000 views/sec. That's a view:upload ratio of ~10,000:1 — the most read-heavy system in this whole set, and the single number that licenses every read-optimized decision that follows. Name it early.
  • Ingest + storage: ~6 uploads/sec × ~1 GB raw ≈ ~6 GB/s straight into object storage (bytes bypass your app servers). That's ~500 TB/day raw, and transcoding multiplies it — one source becomes a ladder of renditions (resolutions × codecs), so stored bytes are a small multiple of raw: order hundreds of PB/year.
  • Egress — the real cost center: a view doesn't transfer a file, it holds a stream. Picture millions to tens of millions of concurrent viewers at ~5 Mbps for HD: ~5M × 5 Mbps ≈ ~25 Tbps. No origin on earth serves tens of terabits per second — which is precisely why the CDN is the read architecture, not an optimization bolted on later.
  • Metadata is a rounding error: a video's metadata row is a few hundred bytes, so even a billion videos is well under a TB. The database is never the constraint here.

Play with the numbers:

Storage & egress calculator

interactive
501.5 TB
Raw ingest / day
50.1 PB
Egress / day
4.6 Tbps
Egress bandwidth
501.5 MB
Metadata / day

At YouTube scale these climb to petabytes of storage and terabits per second of egress, while the metadata stays a rounding error — which is why the design is built around object storage and a CDN, not the database.

Conclusion: storage and egress bandwidth are the cost centers; the metadata DB is trivial by comparison. Say that out loud — it reframes the whole design around object storage, transcoding, and the CDN.

3. API

POST /videos/upload-url   { fileName, parts } -> { uploadId, partUrls[] }   // presigned multipart
PUT  <partUrl>            <chunk bytes>        -> { ETag }                   // upload each part
POST /videos              { uploadId, title, parts:[{partNumber, ETag}] } -> { videoId, status }
GET  /videos/{videoId}    -> { metadata, status, manifestUrl }              // manifest when ready

The client uploads the raw file directly to object storage in parts (so bytes never pass through your app servers), then finalizes the post. GET /videos/{id} returns status: processing until transcoding finishes, then the manifest URL the player needs.

4. The two subsystems

Here's the shape of both halves. The write path (top) takes an upload into object storage, fans it through the transcoding fleet, and writes manifests back to metadata; the read path (bottom) is a thin metadata service plus the CDN that actually serves the bytes. They share nothing but the metadata store — which is exactly why you scale them independently.

Two subsystems
ClientObject storeS3TranscodequeueWorker fleetCDNMetadataserviceMetadata DBuploadcompletepushwatchlookupstream
Write path: a resumable upload to S3, a completion event enqueues transcoding, a worker fleet writes renditions back and pushes them to the CDN. Read path: the metadata service returns a manifest and the player streams segments from the CDN.

5. The write path: upload and transcode (the crux)

Resumable upload. A raw video is gigabytes, and mobile uploads drop, so use a multipart/resumable upload: the client splits the file into chunks, uploads each to object storage and gets back an ETag per part, and on an interruption it re-fetches which parts already exist and resumes — not restart. The completion call assembles the parts and fires an event.

The transcoding pipeline is a DAG. A transcoding orchestrator turns one upload into a graph of jobs:

  1. Segment the source into short chunks (a few seconds each).
  2. Transcode each segment, in parallel across the worker fleet, into multiple codecs and resolutions — H.264 (universal), H.265/VP9 (smaller, newer), AV1 (smallest, most CPU). Encoding for more codecs costs CPU but saves egress; that's the trade.
  3. Generate manifests (HLS and DASH) listing the segments per rendition.
  4. Mark ready — write the manifest URLs to metadata and flip status from processing to ready.

Running segments in parallel is what makes a long video transcode in minutes, not hours. (Netflix takes this furthest with per-title encoding — tuning the rendition ladder to each video — producing on the order of a hundred streams per title.)

Upload and transcode pipeline
resumable uploadfinalize uploadsegment sourcefan out transcode jobswrite renditionswrite HLS / DASH manifestspush to edgesstatus = readyClientS3OrchestratorWorkersCDNMetadata
The upload completes to S3; the orchestrator segments the source and fans out parallel transcode jobs; workers write renditions back, manifests are generated, content is pushed to the CDN, and the video is marked ready.

6. The read path: adaptive bitrate streaming

Frame delivery as a Bad → Good → Great progression:

  • Bad — full download: the player downloads the whole file before playing. Huge wait, wasted bytes if the viewer leaves.
  • Good — segmented download: play segment 1 while fetching segment 2. Fast start, but one fixed quality regardless of the network.
  • Great — adaptive bitrate (ABR): the player measures bandwidth per segment and steps the resolution up or down, so it starts at a low bitrate (fast first frame) and climbs to HD when the connection allows, never stalling.

On watch, the client calls the metadata service for the video info and manifest, then streams segments from the CDN. Your origin serves almost no video bytes — the CDN absorbs the egress. Real systems run deep, multi-tier CDNs; Netflix's Open Connect places caching appliances inside ISP networks so a popular video is served a few milliseconds from the viewer, with the origin only filling the edge on a miss.

7. Data model & storage

DataStoreWhy
Raw + transcoded videoObject storage (S3) + CDNCheap, durable blob storage; CDN for global delivery
Video metadataWide-column (Cassandra) by videoIdSmall records, key lookups, leaderless replication; ~hundreds of millions of rows
Search indexElasticsearch on title/descriptionTitle search is a different access pattern from key lookup
View countsAsync counters (queue → batch increment)Avoid a hot-row write per view; eventual consistency is fine

View counts deserve their own note: never UPDATE ... SET views = views+1 on the hot path — a viral video would serialize on one row. Instead fire each view into a queue/stream, aggregate in batches (often approximately), and write the rolled-up count back periodically. The number you see is eventually consistent, which is fine for views.

8. Scaling the read metadata: partitioning, thumbnails & cache

The CDN handles the video bytes; the metadata service still serves ~58,000 reads/sec of "give me this video's info and manifest." Three pieces keep that cheap.

Partition by videoId. The dominant read is "watch video X" — a lookup by videoId — so shard the metadata store on hash(videoId) and that read is always single-shard. The two access patterns that don't key on videoId — "list a channel's uploads" and "search by title" — become scatter-gathers, so serve them from purpose-built secondary structures instead: a channelId → [videoIds] index for the channel page, and Elasticsearch for title search. Sharding by channelId instead would make the channel page single-shard but turn every watch into a scatter-gather — the wrong trade when watches outnumber channel-page loads by orders of magnitude. Use consistent hashing so adding or losing a shard remaps only a slice, and generate videoId the way Instagram generates a post id — a Snowflake-style, time-sortable id minted with no central counter (see the Instagram write-up).

Thumbnails are a separate, harder read path than the videos themselves. A watch page shows ~20 thumbnails but plays one video, and each video carries several thumbnails at a few KB each — so thumbnail requests dwarf video requests while each object is tiny. Millions of tiny files punish a filesystem with seek amplification (a disk seek per thumbnail), so don't store them as loose files: pack many into larger blocks in a key-value store, keep the hot ones in a memory cache, and serve them from the CDN like any other static asset. The principle — pack small objects, cache aggressively — is the point, not the specific store.

Cache the hot metadata rows. Video popularity is a brutal power law, so put a row cache (Redis/Memcache, LRU) in front of the metadata store; the 80/20 rule does the rest — caching the hot working set absorbs the vast majority of those 58,000 reads/sec, and the store sees only cold-tail lookups. A metadata row is a few hundred bytes, so even caching the hot ~100M videos is tens of GB — one replicated cache node, sized to ~70% utilization.

9. Durability & failure modes

Transcode worker dies mid-job

A worker can crash partway through. Because transcoding is a DAG of independent segment jobs pulled from a queue with visibility timeouts, a failed segment is simply retried by another worker — only that segment, not the whole video. Idempotent jobs (keyed by segment) make retries safe.

Transcoding backlog on an upload spike

A surge of uploads backs up the transcode queue, delaying when videos go live. Autoscale the worker fleet off queue depth, and prioritize: produce the most common rendition (and a low one for fast start) first so the video is watchable quickly, then backfill the rest.

Storage explosion

Every video becomes many renditions, multiplying storage. Tier cold/old videos to cheaper storage classes, and don't pre-generate every resolution for unpopular videos — transcode the long tail lazily on first request.

CDN cold cache on a viral video

A brand-new viral video isn't in edge caches yet, so the first requests miss to origin. Use origin shielding (a mid-tier cache) so the origin is hit once, not once per edge, and pre-warm edges for anticipated spikes (premieres).

Losing a storage node — and how 'durable' is actually achieved

"High durability" is a mechanism, not a wish. Object storage keeps each video across multiple devices/AZs via replication or erasure coding (Reed–Solomon: split into k data + m parity shards, survive any m losses at a fraction of 3× replication's cost) — that's what makes "never lose an upload" true. The metadata store replicates too (factor ~3); a dead node's range is reassigned by consistent hashing and a replica promoted, so a node loss is a blip, not data loss. Replicate both the object store and the metadata cross-region (the CDN is already global) so "never lose an upload" survives a whole region, not just an AZ.

Read-your-writes after upload

Metadata writes hit a primary and replicate to followers a moment later, so immediately after publishing, a read from a follower may not see the new video. Route the uploader's own reads to the primary (or their cache) so creators always see their just-published video, and accept eventual consistency for everyone else.

10. Trade-offs, follow-ups & what to say at each level

Trade-offs to name unprompted: more codecs (CPU at transcode time) vs less egress (bandwidth at serve time); storage cost vs lazy/tiered transcoding; shard metadata by videoId (single-shard watches) vs by channelId (single-shard channel pages); exact vs approximate, eventually-consistent view counts; CDN cost vs origin impossibility.

Follow-ups a senior interviewer asks next: deduplicate re-uploads (fingerprint each upload with a perceptual/content hash — the Content-ID idea — inline at upload so you never transcode and store the same video twice; for a partial match, chunk and keep only the new segments), cross-device resume (store the watch offset server-side so a viewer picks up on their phone where they left off on the TV), live streaming (low-latency HLS, a very different pipeline), content moderation (scan on ingest), DRM for licensed content, exact view counts for monetization (a reconciliation pipeline), and recommendations.

What to say at each level. Mid: object storage + CDN + async transcoding + a metadata DB. Senior: resumable upload, the transcoding DAG, ABR/HLS, the CDN as the read architecture, async view counts, the ~10,000:1 read:write ratio. Staff+: per-title encoding and the codec/egress trade, Open-Connect-style ISP edges, metadata partitioning by videoId with consistent hashing, the thumbnail small-file problem, erasure-coded durability, storage tiering, origin shielding, and the dedup/moderation/DRM/live follow-ups — unprompted.

Why interviewers love this one

It forces the candidate to separate a write-heavy media pipeline from a read-heavy metadata service, to reach for object storage + CDN + async transcoding instead of a database, and to reason about cost at petabyte/terabit scale. It's the same 7-step framework; the bottleneck just moves from the database (as in the URL shortener) to storage and bandwidth.


Amit Singh is a Senior SDE at Amazon, a Claude Certified Architect, and the instructor at AlgoEngineer. Media systems like this reward reasoning about cost at scale; bring your own walkthrough to one of the live mock interviews in our System Design course.

Design YouTube in a system design interview

The moves for a storage- and bandwidth-bound video system.

  1. 1

    Split into two subsystems

    A write-side video pipeline (upload → transcode → store → CDN) and a read-side metadata service. Say this first; it organizes the whole answer.

  2. 2

    Estimate to find the cost centers

    Uploads × raw size → ingest; × renditions → storage; views × delivered size → egress bandwidth. Conclude storage and egress dominate, the DB is trivial.

  3. 3

    Design the upload

    Resumable multipart upload directly to object storage; completion enqueues a transcoding job.

  4. 4

    Design the transcoding pipeline

    A DAG of jobs — segment the file, transcode each segment to multiple codecs/resolutions in parallel, write manifests, mark ready.

  5. 5

    Design the read path

    Metadata service returns a manifest; the player streams segments from the CDN with adaptive bitrate.

  6. 6

    Name the hard parts

    Transcoding cost, storage explosion + tiering, the CDN, async view counts, and failure modes.

Frequently asked questions

Why is designing YouTube really two systems?
Because the write side and the read side have nothing in common. The write side is a heavy, asynchronous media pipeline — resumable upload to object storage, then a fleet of transcoding workers turning one raw file into many renditions. The read side is a lightweight metadata service plus a CDN that serves the actual video bytes. Treating video like normal database data is the classic mistake; the design is dominated by storage, transcoding, and delivery, not by the database.
How does adaptive bitrate streaming (HLS/DASH) work?
During transcoding, each video is cut into short segments (a few seconds each) and encoded at multiple resolutions/bitrates. The server produces a manifest listing those segments. The player downloads the manifest, then requests segments at a bitrate matched to the viewer’s measured bandwidth, stepping up or down per segment as the network changes — so playback starts fast and never stalls. HLS and DASH are the two common manifest formats.
Why must transcoding be asynchronous?
Transcoding a gigabyte-scale file into many codec/resolution combinations is CPU-heavy and slow — seconds to minutes. You cannot make the uploader wait, so the upload completes, an event enqueues a transcoding job, and a worker fleet processes it in the background while the video shows as "processing." This also lets you autoscale the workers against the queue depth to absorb upload spikes.
How does YouTube count views without a hot-row write per view?
Never with UPDATE views = views + 1 on the hot path — a viral video would hammer one row. Instead, view events are fired into a queue/stream and aggregated in batches (and often approximately), then the count is written back periodically. The displayed count is eventually consistent, which is completely acceptable for a view counter.
How is the video actually delivered to a billion viewers?
Through a CDN, never from origin servers — serving petabytes of egress from origin would be impossible and ruinously expensive. Netflix and YouTube run deep CDNs (Netflix’s Open Connect places caching appliances inside ISP networks) so popular content is served from an edge a few milliseconds from the viewer. The origin only fills the edge on a cache miss.
How do you shard the video metadata, and why are thumbnails a separate problem?
Shard the metadata store by videoId, because the dominant read is "watch video X" — a single-key lookup — so hashing on videoId keeps every watch single-shard, with consistent hashing to rebalance. The access patterns that do not key on videoId (a channel’s uploads, title search) become scatter-gathers, so you serve them from a channelId→videoIds index and an Elasticsearch index instead. Thumbnails are a separate, higher-QPS read path: a page shows ~20 tiny thumbnails but plays one video, and millions of small files cause disk seek amplification — so you pack many into larger blocks in a key-value store, cache the hot ones in memory, and serve them from the CDN.

Ready to Ace Your Interviews?

Live, cohort-based interview prep taught by a working FAANG engineer — weekly mock interviews, lifetime access, and a 7-day money-back guarantee.