System design · 16 of 20

Designing YouTube

Resumable upload, the transcoding ladder and chunk-parallel encoding, packaging, the long tail, CDN delivery, adaptive bitrate, view counts and live streaming.

Updated 2026-09-18
On this page

Video is the only system in this series where the bytes themselves are the problem. A social feed moves kilobytes; this moves roughly a hundred terabits a second, and every one of those bits was produced by a transcoding farm that had to guess, at upload time, which of eight quality levels anyone would ever watch. The two halves of the design barely touch: an enormous batch pipeline that turns one file into hundreds, and a delivery network whose only job is to never fetch the same segment twice.

The map

Read this first when short on time. Every branch is a section below.

Figure 1. The whole article on one page. Every branch is a section below; fold what you know, open what you do not.

What it must do, and at what size

It must accept a video from an unreliable device, turn it into something every screen and network can play, deliver it worldwide at a quality the connection can sustain, and count what happened. It must not, in this article, cover search and recommendations in depth — those are the subject of the analytics and search articles — nor the curated-catalogue problems that the Netflix article takes up.

Ingest     500 hours of video uploaded per minute
           = 30,000 hours/hour ~= 2 GB/hour of source -> ~60 TB/hour, ~1.4 PB/day

Egress     1 billion hours watched/day at an average ~2 Mbps
           1e9 h x 3,600 s x 2 Mb = 7.2e12 Mb/day ~= 900 PB/day
           ~83 Tbps average, ~150-200 Tbps at peak

Ratio      egress is roughly 600x ingest -> delivery is the business
Transcode  ~8 renditions per video, output ~1.5-2x the source bytes
           encoding one hour of video costs hours of CPU per rendition
Tail       a small fraction of videos take most views
           the median video is watched a handful of times

Two facts dominate. Egress is six hundred times ingest, so the delivery network is the product's cost structure and its engineering centre of gravity. And popularity is a power law that nobody can predict at upload time, which means encoding everything at every quality wastes an enormous amount of compute on videos nobody will watch, while encoding nothing in advance makes the first viewer wait.

flowchart TD
  U["creator uploads"] --> IN["upload service, resumable"]
  IN --> OR[["original in cold storage"]]
  IN --> Q[["transcode job graph"]]
  Q --> TR["encoding farm, chunk parallel"]
  TR --> PK["packager: segments and manifests"]
  PK --> ST[["rendition storage, origin"]]
  ST --> RG["regional cache"]
  RG --> ED["edge cache, inside ISPs"]
  ED --> V["viewer, adaptive bitrate client"]
  V --> EV[["playback and view events"]]
Figure 2. The whole system. The upload half runs once per video and the delivery half runs a billion hours a day, which is why they are sized, operated and paid for independently.

Upload

The upload path is short but has to survive the worst client conditions in this series: a phone on mobile data sending several gigabytes.

Uploads are chunked and resumable. The client asks to start an upload, receives a URL and an id, sends fixed-size parts, and can at any point ask how much the server has — so a dropped connection resumes rather than restarts. The video becomes visible only when every part has landed and the server has assembled and verified them, which is the same ask-then-send-then-commit shape as the file sync design, for the same reasons.

Before anything expensive begins, the file is probed: container, codecs, resolution, frame rate, duration, and whether it is actually decodable. Rejecting a malformed file here costs a second; discovering it after scheduling eight encoding jobs costs hours of CPU. A content hash also catches an exact re-upload, which is common enough to be worth handling.

The original is kept forever, in the cheapest available tier. It is the only thing that cannot be regenerated, and it is what allows the entire catalogue to be re-encoded years later when a better codec arrives — which is a real operation that saves a meaningful fraction of global bandwidth each time.

Transcoding

One upload has to become many files, because the devices and networks watching it have nothing in common. A 4K master is useless to a phone on a slow connection, and a 240p version is unwatchable on a television.

The output is a ladder: roughly eight steps from 144p at a few hundred kilobits to 4K at tens of megabits, each with a resolution and a bitrate chosen together, because encoding 1080p at 300 kbps produces something worse than 360p at the same rate. Each rung is usually produced in more than one codec — a universally supported one for compatibility and a newer one that is meaningfully more efficient for the clients that can decode it.

flowchart TD
  S["source file"] --> P["probe and split at keyframes"]
  P --> C1["chunk 1"]
  P --> C2["chunk 2"]
  P --> C3["chunk N"]
  C1 --> E["encoder workers, one rendition per chunk"]
  C2 --> E
  C3 --> E
  E --> J["stitch chunks per rendition"]
  J --> V["verify: duration, drift, quality score"]
  V --> PK["package into segments and manifests"]
Figure 3. Chunk-parallel transcoding. Splitting at keyframes lets a thousand workers encode one video at once, turning hours of serial CPU time into minutes of wall clock.

The trick that makes this tractable is chunk-parallel encoding. Video is split at keyframes into independent pieces, each encoded separately, and the results are concatenated. Because each chunk starts at a keyframe, the join is seamless. A video that would take four hours to encode serially finishes in minutes given enough workers — and the work is embarrassingly parallel, which is exactly the shape a batch fleet wants.

Scheduling matters as much as the encoding. The jobs form a graph per video, and the order is chosen so that something is watchable as soon as possible: produce a mid-quality rendition first, publish, then fill in the rest. A creator with a large audience gets priority, because their video will be watched within seconds of publication and an unknown creator's may not be watched at all.

In the wild
  • H.264 remains the universal fallback; VP9 and AV1 deliver the same quality in substantially fewer bits, which pays for the extra encoding cost many times over on popular content.
  • Netflix's per-title encoding work showed that a fixed ladder wastes bits: an animated film and a dark action film need very different bitrates for the same perceived quality.
  • Encoding farms at this scale are measured in CPU-years per day, and are the classic workload for preemptible or spot capacity, since any chunk can be retried.

Packaging

A rendition is not shipped as one file. It is cut into segments of a few seconds, each beginning with a keyframe so that it can be decoded independently, and described by a manifest listing every rendition and every segment.

That structure is what makes adaptive streaming possible: a player can fetch segment 41 at 720p and segment 42 at 1080p and splice them, because each is self-contained. It is also what makes the CDN's job easy — a segment is an immutable file with a stable URL, so it can be cached forever, which is the single most important property in the delivery path.

The two manifest formats in use say the same thing in different syntax, and the practical answer is to package once into a common segment format and generate both manifests, rather than encoding twice.

Rendition
One quality level of a video: a resolution, a bitrate and a codec. A video has several, and the client switches between them.
Segment
A few seconds of one rendition, starting at a keyframe, stored as an immutable file. The unit of caching and of adaptation.
Manifest
The index a player fetches first: which renditions exist, and the URLs of their segments.

Storage and the long tail

Here is the decision that saves the most money in the entire design, and it comes from the shape of the popularity curve rather than from any clever mechanism.

Most videos are watched very few times. A tiny fraction are watched enormously. And crucially, nobody knows which is which at upload time — popularity is unpredictable, and it changes when a video resurfaces years later. Encoding every video into every rung of the ladder means spending the majority of the encoding budget on renditions that will never be requested.

The answer is lazy transcoding. Produce a small number of renditions eagerly — enough to play on a phone and on a laptop — and generate the expensive high-resolution ones on first demand, accepting that the very first viewer of a 4K rendition waits, or is served a lower quality while it is produced. Once produced, it is cached like anything else. The same reasoning tiers storage: hot renditions on fast storage, cold ones evicted and regenerated from the original if they are ever wanted again.

flowchart TD
  U["video uploaded"] --> E["encode the cheap renditions now"]
  E --> P["publish, playable on phone and laptop"]
  P --> W{Requested at 4K?}
  W -- "no, most videos" --> N["never encoded, costs nothing"]
  W -- "yes" --> L["encode on demand, first viewer waits"]
  L --> C["cached like any other rendition"]
Figure 4. Lazy encoding of the tail. Popularity is unpredictable at upload, so the expensive renditions are produced only when somebody actually asks for one.
Tip

The general form of this idea is worth remembering beyond video: when the cost of precomputing every possibility is high and the distribution of demand is heavily skewed and unpredictable, compute the cheap common cases eagerly and the expensive rare ones on demand. Regenerability is what makes it safe — the original is the source of truth and everything else is a cache.

Delivery

Roughly a hundred terabits a second is not served from a datacentre. It is served from thousands of caches placed as close to viewers as possible, and the entire economics of the product is the fraction of bytes that come from the nearest one.

The hierarchy is edge, regional, origin. A player requests a segment from its nearest edge cache; a miss fills from a regional cache, and a miss there fills from origin. Because segments are immutable, every one of those caches can hold them indefinitely with no invalidation logic at all — the hardest problem in caching simply does not exist here.

flowchart TD
  V["viewer"] --> E["edge cache, inside the ISP"]
  E -- "miss" --> R["regional cache"]
  R -- "miss" --> O[["origin storage"]]
  O --> R
  R --> E
  PP["predicted-popular titles"] --> E
Figure 5. Three tiers and two fill modes. Content expected to be popular is pushed to the edge in advance; everything else arrives because one viewer missed.

Placement has two modes. Push: a video expected to be popular — a major release, a channel with millions of subscribers — is copied to edge caches before anyone asks, so the first viewer in each region already gets a hit. Pull: everything else arrives at the edge because somebody watched it. The push list is a prediction problem, and getting it right matters because a miss at the edge for popular content multiplies upstream traffic by the number of edges.

The most consequential piece of engineering here is not in the software: it is placing caches inside internet service provider networks. A cache in the ISP's own datacentre means the bytes never cross a paid transit link, which is better for the ISP's costs and for the viewer's latency simultaneously. It is why video platforms run hardware programmes rather than simply buying more bandwidth.

Adaptive bitrate

The server does not decide the quality; the player does, one segment at a time. This is the right design because only the client can observe what is actually happening on the last mile, which changes as someone walks between rooms.

flowchart TD
  S["segment finished downloading"] --> M["measure throughput and buffer level"]
  M --> B{Buffer healthy?}
  B -- "low" --> D["step down, protect playback"]
  B -- "comfortable" --> T{Throughput above the next rung?}
  T -- yes --> U["step up one level"]
  T -- no --> K["stay"]
  D --> N["request the next segment"]
  U --> N
  K --> N
Figure 6. The client's decision, repeated every few seconds. Buffer level protects against stalling and measured throughput drives the climb, which is why real algorithms use both.

Two families of algorithm exist and both have a characteristic failure. Throughput-based selection reacts quickly and oscillates, because one fast segment convinces it to step up and the resulting larger segment convinces it to step back. Buffer-based selection is stable — it simply keeps the buffer full — but is slow to take advantage of a genuinely improved connection. Real players blend them, with hysteresis so that a step up requires sustained evidence and a step down happens immediately.

The most important moment is the start. Time to first frame determines whether a viewer stays, so playback begins at a low rendition and climbs within a few seconds, rather than waiting to measure the connection properly. This is the same asymmetry as everywhere else in this article: a slightly worse picture is always better than a delay, and a stall is the worst outcome available.

Counting views

A view count looks like an integer and is a policy. What counts as a view — thirty seconds, some fraction of the duration, a deliberate play rather than an autoplay — has to be defined before it can be implemented, and the definition drives payouts, so it is also an adversarial problem.

Mechanically, the player sends periodic heartbeats while playing rather than one event on start. Heartbeats give watch time, which is the signal that ranking actually uses, and they make a view a duration rather than a click. The events flow into the same shape of pipeline as everything else in this series: a partitioned log, a stream aggregation into per-video per-hour counters, and a serving store for the dashboard.

Two adjustments make it affordable. Counters are sharded, because a video going viral concentrates tens of thousands of increments a second on one key. And displayed counts are approximate and delayed above a threshold — nobody distinguishes 2.4 million from 2.41 million — while the creator's own analytics are exact and reconciled from the log, after fraud filtering has had time to run.

Live, in one section

Live streaming is the same pipeline with the time removed, and that changes three things.

There is no time to encode ahead: the ladder is produced in real time as the stream arrives, which means encoding must keep up with the input or fall behind permanently, and chunk-parallel tricks no longer help because there is only one chunk at a time. There is a latency budget measured in seconds rather than milliseconds, and the segment length sets the floor — four-second segments mean at least four seconds of delay, which is why low-latency variants deliver partial segments as they are produced rather than waiting for a complete one. And the DVR window — the ability to seek backwards during a live stream — is just the recent segments kept around, which makes it nearly free.

Everything else is identical: segments, manifests, edge caching, adaptive bitrate. The manifest is simply rewritten as new segments appear, and the player polls it.

What breaks first

  1. Encoding capacity at upload spikes. Uploads are bursty and encoding is expensive, so a backlog forms and videos publish late. Relieved by lazy encoding, by priority queues that serve large channels first, and by the fact that the work runs happily on preemptible capacity.
  2. Edge cache hit ratio. The number that determines the bandwidth bill. It degrades when the catalogue grows faster than the caches, and it is improved by better push prediction rather than by more storage.
  3. Peering and transit capacity. Physical, expensive, and negotiated months in advance. It is the reason caches go inside ISP networks.
  4. Metadata for the tail. Billions of videos each with manifests, renditions, thumbnails and rights information is a large database problem hiding behind a much more visible bandwidth problem.

The judgement running through the whole design: playback beats everything. Serve a lower rendition, serve from a further cache, serve a slightly stale manifest — but never stall the video, because a stall is the one failure a viewer will not forgive.

Recap

  • Egress is roughly six hundred times ingest, so the delivery network is the cost structure and the centre of gravity of the entire design.
  • Uploads are chunked, resumable and committed only when complete, with the file probed early so a malformed input does not consume hours of encoding.
  • The original is kept forever in the cheapest tier: it is the only irreplaceable artefact and it allows the catalogue to be re-encoded when better codecs arrive.
  • One upload becomes a ladder of about eight renditions, resolution and bitrate chosen together, usually in more than one codec.
  • Chunk-parallel encoding splits at keyframes so a thousand workers can encode one video at once, turning hours of CPU into minutes of wall clock.
  • Schedule so that something is watchable quickly, and give priority to uploads that will be watched immediately.
  • Renditions are packaged into few-second segments starting at keyframes, described by a manifest — which is what makes both adaptation and infinite caching possible.
  • Popularity is a power law nobody can predict at upload, so encode the cheap common renditions eagerly and the expensive ones lazily on first demand.
  • Segments are immutable, so every cache can hold them forever and cache invalidation — normally the hardest part of caching — does not exist here.
  • Delivery is edge, regional, origin, with popular content pushed in advance and everything else pulled; caches inside ISP networks are what keep bytes off paid transit.
  • The client picks the quality per segment, using buffer level to avoid stalling and measured throughput to climb, with hysteresis to stop oscillation.
  • Start at a low rendition and climb, because time to first frame determines whether the viewer stays.
  • A view is a policy before it is an integer; heartbeats give watch time, counters are sharded, public counts are approximate and creator analytics are exact after fraud filtering.
  • Live is the same pipeline without the time: real-time encoding, segment length as the latency floor, and a DVR window that is nearly free.
  • The ladder of bottlenecks is encoding capacity, edge hit ratio, peering, then long-tail metadata — and playback beats everything.

Questions

Try answering each one out loud before opening it. Lead with the one-line answer, then the reasoning, then one detail from a real system.

Size a video platform taking 500 hours of upload a minute and a billion hours of watching a day.

Ingest is roughly 1.4 petabytes a day, while egress at an average two megabits a second is around 900 petabytes a day — about 83 terabits a second average and up to twice that at peak, so egress exceeds ingest by roughly six hundred times.

  • That ratio means the delivery network, not the storage or the encoding, is the product's cost structure.
  • Each video becomes about eight renditions totalling more than the source, so stored bytes are a multiple of ingest.
  • Encoding one hour of video costs hours of CPU per rendition, which is why the farm is measured in CPU-years.

Doing this arithmetic first is what tells you to spend the design effort on caching rather than on the upload path.

Why must uploads be resumable, and how is that implemented?

Because the client is often a phone sending gigabytes over mobile data, where a dropped connection is normal; the upload is chunked, the server tracks which parts arrived, and the client can ask how much is present and continue from there.

  • The video becomes visible only when every part has landed and been verified, so nothing is half-created.
  • Probing the file immediately rejects malformed input before any encoding is scheduled.
  • A content hash catches exact re-uploads cheaply.

It is the same ask-then-send-then-commit shape as chunked file sync, and for the same reasons.

Why keep the original file forever?

Because it is the only artefact that cannot be regenerated, and keeping it means the entire catalogue can be re-encoded years later when a more efficient codec becomes widely supported.

  • Every rendition is derived, so losing one costs CPU rather than content.
  • A codec generation that saves twenty percent of bits is worth an enormous amount at this egress volume.
  • The original lives in the cheapest storage tier, since it is read rarely.

Treating derived data as a cache and the source as the truth is what makes lazy and repeated re-encoding safe.

What is a bitrate ladder and why are resolution and bitrate chosen together?

A ladder is the set of renditions a video is encoded into, roughly eight steps from very low quality to 4K; resolution and bitrate are paired because encoding a high resolution at too low a bitrate looks worse than a lower resolution at the same rate.

  • The client needs several rungs so it can adapt as the connection changes.
  • Each rung is often produced in more than one codec: a universal one and a more efficient newer one.
  • A fixed ladder wastes bits, since different content needs different bitrates for the same perceived quality.

Per-title encoding, which tunes the ladder to the content, was Netflix's well-published improvement on the fixed approach.

How can a four-hour encoding job finish in minutes?

By splitting the video at keyframes into independent chunks, encoding them on many workers in parallel, and concatenating the results — the join is seamless because each chunk begins at a keyframe.

  • The work is embarrassingly parallel, so it scales with the size of the fleet.
  • Any chunk can be retried independently, which makes preemptible capacity a good fit.
  • A verification step checks duration, drift and a quality score before the result is published.

This is the mechanism that makes publishing within minutes of upload possible at all.

Why not encode every video into every rendition?

Because popularity is a power law that nobody can predict at upload time, so most of the encoding budget would be spent producing high-resolution renditions of videos almost nobody watches.

  • Encode a small set eagerly so the video plays on a phone and a laptop immediately.
  • Produce the expensive rungs on first demand, accepting that the first viewer waits or gets lower quality briefly.
  • Cold renditions can be evicted and regenerated from the original, because the original is the source of truth.

The general pattern — eager for cheap common cases, lazy for expensive rare ones — applies well beyond video.

Why is video unusually friendly to caching?

Because a segment is an immutable file with a stable URL: once produced it never changes, so every cache in the hierarchy can keep it indefinitely and cache invalidation simply does not arise.

  • Segments are a few seconds long and start at a keyframe, so they are independently decodable and independently cacheable.
  • The manifest is the only mutable object, and it is small.
  • This is why the delivery tier can be a simple hierarchy of caches rather than a coordinated system.

The hardest problem in caching being absent is what allows the delivery network to be enormous and still operationally boring.

What is the difference between pushing and pulling content to the edge?

Pushing copies content to edge caches before anyone requests it, so the first viewer in each region gets a hit; pulling fills the cache only when somebody watches, which means one miss per edge.

  • Push is used for content predicted to be popular, where a miss would multiply upstream traffic by the number of edges.
  • Pull is right for the long tail, where pre-positioning would waste far more storage than it saves.
  • Choosing what to push is a prediction problem and a meaningful lever on the bandwidth bill.

Cache hit ratio, not storage or CPU, is the metric that determines the economics of the whole platform.

Why put caches inside ISP networks?

Because bytes served from a cache inside the provider's own network never cross a paid transit link, which lowers cost for both sides and shortens the path to the viewer at the same time.

  • It aligns incentives: the ISP saves on transit and gets better quality for its subscribers.
  • It is a hardware and partnership programme rather than a software problem, which is unusual for a platform's most important optimisation.
  • Peering and transit capacity is negotiated months ahead, so it cannot be scaled reactively.

This is the single largest lever on delivery cost at this scale, and it lives outside the codebase entirely.

Why does the client choose the bitrate rather than the server?

Because only the client can observe the last mile, which is where the variability is: signal strength, contention on a home network, and a user walking between rooms are all invisible to the server.

  • The client measures how long each segment took and how much buffer it holds, and picks the next segment's rendition accordingly.
  • Independently decodable segments are what make switching mid-playback possible.
  • It also means adaptation logic can improve by shipping a new player, without touching the delivery tier.

The server's contribution is offering a good ladder and serving segments quickly enough that measurement is meaningful.

Compare throughput-based and buffer-based adaptation.

Throughput-based selection reacts quickly to changes and tends to oscillate, since a fast segment triggers a step up whose larger segment then triggers a step down; buffer-based selection is stable but slow to exploit a genuinely better connection.

  • Real players combine them, using buffer level as protection and throughput as the signal for climbing.
  • Hysteresis — requiring sustained evidence to step up, stepping down immediately — removes most of the oscillation.
  • Playback starts low and climbs, because time to first frame drives abandonment.

The asymmetry is deliberate: a slightly worse picture is always preferable to a stall.

How do you count views at this scale?

The player sends periodic heartbeats while playing rather than one event at the start, those flow into a partitioned log, and a stream job aggregates them into per-video counters — with sharded counters to survive a viral video.

  • Heartbeats give watch time, which is the signal ranking actually uses, and make a view a duration rather than a click.
  • Public counts are approximate and delayed above a threshold, since nobody distinguishes 2.4 from 2.41 million.
  • Creator-facing analytics are exact and reconciled from the log once fraud filtering has run.

What counts as a view is a policy decision with payout consequences, which makes it an adversarial problem as much as a technical one.

How does live streaming change the design?

Encoding must happen in real time, so chunk-parallel tricks no longer help; the segment length sets a latency floor; and the DVR window is simply the recent segments kept around.

  • Falling behind in live encoding is permanent, unlike a batch backlog that eventually drains.
  • Low-latency variants deliver partial segments as they are produced rather than waiting for a complete one.
  • Everything downstream — segments, manifests, edge caching, adaptive bitrate — is unchanged.

The manifest becomes the mutable object, rewritten as each segment appears and polled by players.

An upload spike creates an encoding backlog. What do you do?

Publish a mid-quality rendition first and fill the ladder later, prioritise creators whose videos will be watched immediately, and lean on the fact that encoding runs happily on preemptible capacity that can be scaled up quickly.

  • Lazy encoding of the expensive rungs removes most of the backlog's volume.
  • Chunks can be retried anywhere, so losing a worker mid-job costs one chunk.
  • The failure mode is videos publishing late, which is far better than uploads failing.

Designing the job graph so that partial completion is publishable is what makes graceful degradation possible here.

What is the metadata problem hiding behind the bandwidth problem?

Billions of videos each with manifests, rendition lists, thumbnails, rights and availability information make a very large database, and it is read on every playback start even for videos nobody watches twice.

  • The long tail means caching helps less here than it does for segments.
  • Rights and availability vary by country, so the answer is per request rather than per video.
  • It is a small-row, high-cardinality workload — the opposite shape of the byte pipeline beside it.

This is the part of a video platform that most designs skip and that grows fastest with catalogue size.

What should degrade first when the system is under stress?

Quality, then freshness, then features — but never playback: serve a lower rendition, serve from a more distant cache, serve a slightly stale manifest, and keep the video running.

  • A stall is the failure viewers punish hardest, far more than a visibly lower resolution.
  • Recommendations, comments and counts can all fail without stopping the video.
  • Naming this ordering in advance is what makes degradation automatic rather than improvised.

It is the same principle as everywhere in this series: decide what is sacred, and let everything else bend.