A creator uploads one video. Later, millions of viewers around the world want to watch it, each on their own screen, each on their own network, whenever they choose. Build the system that takes the uploaded video, gets it ready to play, and streams it to every one of those viewers smoothly — starting fast, never stalling, and adjusting to whatever connection each viewer happens to have. This is video on demand: upload once, watch later, watch anywhere, watch a lot.
Predict ~1.5 h · Read ~50 min
The problem i
A video service looks like a storage problem and turns out to be a delivery problem. The instinct is to worry about where all that video lives, because video files are big and there are a lot of them. Hold that instinct, because it is pointing at the wrong number. Everything hard about this system turns on two facts, and neither one is about how much disk you buy.
Here is the first hard fact. Storing the library is the easy part. Do the arithmetic and it comes out small. A region's whole catalog is a few thousand titles. Each title, encoded and ready to serve, is a handful of gigabytes. Multiply it out and one region's ready-to-play library is tens of terabytes — a few racks, not a data center. Even when you generously account for many languages, subtitle tracks, and a global footprint, it still lands well under a petabyte. That is a real number, but it is a small number, and no single decision in this design is forced by it. Storing the videos is never the wall.
Here is the second hard fact, and it is where the wall actually is. The same video is watched an enormous number of times, and every one of those views is bytes leaving your system. Watching dwarfs uploading. On television screens alone, in one country, viewers watch more than a billion hours a day, while creators upload something like five hundred hours a minute. That is watching outrunning uploading by roughly a thousand to one. Now turn those watch-hours into bytes on the wire. Every viewer is pulling a steady stream of video — a few megabits every second, continuously, for as long as they watch. Add up a large audience all watching at once and you are pushing terabits per second out of your system. No single server can push bytes that fast. A server's link to the internet tops out orders of magnitude below what a large regional audience demands. So the wall is not disk and it is not requests per second. It is outbound bandwidth, or egress: the sheer rate of bytes leaving the building. Reconciling that firehose with the physical limit of any one machine is the force that shapes the whole design.
Those two facts flip the problem. The scary-sounding part, storage, is the small number. The boring-sounding part, serving the bytes, is the wall. Getting one video ready to play is the easy half. Serving it to millions of people at once, over connections you do not control, is the whole game.
There is a third strand, quieter but real, on the way in. Getting a video ready to play is itself work, and it cannot happen the instant an upload lands. One source video has to be turned into several versions at different qualities so a viewer on a weak connection and a viewer on a strong one can each get a watchable stream. That conversion is heavy, the uploads arrive fast, and a viewer who just hit publish should not sit and wait for it. So the upload path has its own shape to work out. But it is the read path — the bytes going out — that carries the wall, and that is where the design starts once one server stops being enough.
Two timings are worth pinning now, because decisions later turn on them. A durable write to disk, the kind that has to survive a crash, takes about ten milliseconds. Fetching a few seconds of video from a cache sitting close to the viewer is a few milliseconds — one local read. The gap between close to the viewer and back at the origin is the gap the whole read path is built around.
Functional requirements what it must do
Accept a creator's uploaded video and get it ready to play.
Play a video for a viewer, starting fast and without stalling.
Adapt the quality to each viewer's connection, without them managing it.
Serve the same popular video to a huge audience at once.
Store every ready-to-serve video durably.
Non-functional requirements what it must be
Sustain terabits per second of outbound video, with no single machine as the bottleneck.
Keep starting playback fast wherever the viewer is.
Do not make a creator wait for heavy processing.
Keep serving during a partial failure.
Out of scope left out on purpose
The encoding algorithm itself
Live streaming
Content protection
What to recommend and what to show next
Advertising and monetization
How the design works
The finished design, explained one piece at a time: first the contract the system serves, then the smallest version that works, then the version that survives scale — walked use case by use case, one deep dive at a time, with the finished diagram at the end.
The API
The whole product is these endpoints. Every box in the design below exists to serve one of them.
POST /videos
A creator uploads a source video: begin the upload, then send the file itself in resumable chunks, answered once the bytes are safely stored. The creator is told the upload succeeded as soon as it is durable, not when it is finished processing. This happens rarely compared to playback and is never on the hot read path.contract ▾
# A creator begins a resumable upload, then sends the file in chunks.→POST /videos { title: "…", sizeBytes: 2_100_000_000 }
←201 Created { uploadUrl: "/videos/v_82/chunks" }
→PATCH /videos/v_82/chunks (chunk, resumes from the last received byte)
←201 Created # success = bytes are DURABLE, not = encoded
GET play
A viewer's player asks for a video and first gets back a small description of the available qualities and where to fetch the pieces of the stream. In plain terms it is give me what I need to start watching this; the exact shape of that description is what the read path is built around.contract ▾
# The player asks what exists before it fetches any bytes.→GET /videos/v_82/manifest
←200 OK {
rungs: [ { bitrateKbps: 5800, … }, { bitrateKbps: 2350, … }, … ],
segmentSeconds: 6 } # a small manifest, not the video
READ segment
The player then repeatedly fetches short pieces of the video, choosing the quality it can currently sustain. This is the operation that happens billions of times, and making it cheap and local is the whole read-side design.contract ▾
# The player pulls the stream piece by piece, picking its own rung.→ (from the nearest PoP) GET /seg/v_82/r3/00042.ts
←200 OK <~6 s of video> # a cache hit, a few ms away — no per-viewer server decision
part 1 The single-server version
Start with the smallest thing that works: one server that holds an uploaded video and streams it back.
Start with the vocabulary. A creator hands the system an upload — one source video, a large file. A viewer later wants to watch it, using a player — the app or the browser tab on their screen. The player does not download the whole file and then start; it pulls the video in segments, short pieces fetched one after another over HTTP, so playback can begin after the first piece arrives. And the whole collection of videos the service offers is the catalog. Those words — an upload, a source video, a viewer, a player, a segment, the catalog — carry the whole problem.
The day-one design is direct. One server, call it the Origin, does everything. When a creator uploads, the origin stores the source video on its disk. When a viewer wants to watch, the origin reads that video off its disk and streams it back to the player, segment by segment, over HTTP. One box, one copy of each video, both jobs. For a single creator and a handful of viewers on the same continent, this is genuinely fine, and nothing clever is missing.
The whole system on day one.
Following a request
A creator's upload lands
a creator uploads a source video; the Origin stores it on disk and, on one box, would process it inline before it could be watched~10 ms
A viewer watches
a viewer watches; the Origin reads the video off its disk and streams it back, segment by segment, over its own single uplink~10 ms
One hop dominates every path: the client's request crossing the public internet to reach the datacenter. That hop is tens of milliseconds — call it about 30 — and it is set by physical distance, not by anything we build. Everything after it happens inside a single datacenter, where the network between two machines is well under a millisecond — so those numbers are the actual work, not the wait: a durable write to disk about 10 ms, an indexed read about 5 ms. A hop between two datacenters would cost 10–100× the in-datacenter number, which is why the whole path stays in one region.
It just does not survive being pointed at a real audience, and it fails in three separate ways — each of which forces a piece of the real design. The first failure is the one from the brief, and it is the big one. The origin can only push bytes as fast as its link to the internet allows, and that link is small compared to what an audience demands. Picture a video going viral: a hundred thousand people, then a million, all press play within the same hour. Every one of them is pulling a steady stream of a few megabits a second from that one server, continuously. Add them up and the demand is terabits per second. The origin's uplink is orders of magnitude below that. It does not slow down gracefully; it simply cannot move the bytes, and every viewer past the first few thousand gets a stream that stalls or never starts. One machine physically cannot be the source of a large audience's bytes.
The second failure is that the single stored video is the wrong thing to serve. The origin holds one copy at whatever quality the creator uploaded, but viewers are not all on the same connection: a fiber viewer can take a high-bitrate stream, a viewer on a phone in a train tunnel cannot, and sending them the high-bitrate stream anyway makes their player stall constantly. One stored quality cannot serve every connection. The third failure is the upload path itself: the origin accepts the whole gigabyte source in one shot, uploads drop halfway, and the single stored quality is not even what a viewer should be served, so real processing has to happen between the creator uploading and the viewer watching — and on one box that processing happens inline, while the creator waits and while it competes with serving. Those three failures point the same way: the bytes have to be served from many places close to the viewers, the single stored quality has to become several qualities with a way to pick between them, and the upload has to be accepted quickly and processed off to the side, durably. The rest of this design does exactly that, one forced decision at a time — and it starts with the wall.
part 2 The version that survives scale
This section walks the design by use case, in the order you actually have to decide things. First, face the wall head-on: the outbound bytes cannot come from one origin, and fixing that is the heart of the whole design. Then work out what the serving tier actually caches and how a viewer on a variable connection gets a watchable stream — and, along the way, discover that storing the library was never the hard part. Then turn to the upload path: accept a creator's video fast, process it off to the side, and never lose it. And finally, see what happens when a whole region's control plane disappears, and why the serving tier keeps playing anyway. The finished diagram waits at the end.
Two ways through this section
The four decisions below are the heart of this design, and there are two ways to take them. In the design room you make each one yourself and the board reacts to your pick. The write-ups after this point cover the same decisions in full — read them instead, or afterwards as reference.
use case 1 A million viewers at once, and the bytes cannot leave one box
On day one, every viewer's stream left through a single origin's uplink. That holds right up until one title gets popular — and then, as the brief warned, that one uplink becomes the wall the entire design has to get around.
deep dive 1 A million viewers at once, and the bytes cannot leave one box
The question
The origin works until an audience shows up. A popular video is being watched by a huge audience at once, spread across cities and countries. Every viewer is pulling a steady multi-megabit stream, continuously. Together they demand terabits per second of outbound bytes. One origin's link to the internet is orders of magnitude too small to push that. It does not slow down gracefully; past the first few thousand viewers, it simply cannot move the bytes. Where do the bytes come from?
Follow the obvious answer first, because it is what most people reach for: give the origin a bigger pipe. Put it on a fatter uplink, or add a few more origin servers behind a load balancer so there is more outbound bandwidth to go around. The attraction is real — it is the design you already have, scaled up. But the arithmetic kills it. Five hundred thousand people watching at once, each pulling about three megabits a second, is about one-and-a-half terabits per second — from one region. A single high-end serving box tops out around ninety to a hundred gigabits per second of outbound video. That regional demand is more than fifteen times what one box can push, and that is one mid-size region on one popular title. A bigger pipe is not fifteen times bigger; it is an order of magnitude short, and the gap only widens with the audience. Adding a handful of origin boxes does not close a fifteen-to-one gap, and it does nothing about the other problem hiding in the number: those viewers are spread across a continent, and serving all of them from one central location means every byte crosses the whole distance to reach them, slow to start and expensive to carry.
A second instinct is worth killing too, because it sounds sophisticated: put a cache in front of the origin. The trouble is that a plain key-value cache is built to answer lookups faster — it remembers the answer to a question so you do not recompute it. But the origin's problem is not that lookups are slow. It is that bytes cannot leave the building fast enough. A cache sitting next to the origin still has to push every one of those bytes out the same regional uplink. It caches the wrong thing. The ceiling here is bandwidth, not lookup speed, and no amount of remembering answers moves more bytes through a fixed-size pipe. So the answer is neither a bigger pipe nor a cache in one place — it is to spread the serving across many appliances, each close to its viewers, so the aggregate outbound rate is spread across the whole fleet and no single uplink is the ceiling.
A CDN edge tier of PoP caches close to the viewer, the origin serving only fills and misses. Stop serving the bytes from one place. Put the video on a tier of caches spread across the world, sitting close to where the viewers actually are — a CDN edge tier of points of presence, or PoPs. Each PoP is a cluster of serving appliances installed deep in the network, often inside the viewer's own internet provider. A viewer's player does not fetch segments from a distant origin; it fetches them from the nearest PoP, a short hop away. And here is the move that beats the arithmetic: the terabits of demand are no longer one number against one box. They are spread across hundreds of PoPs, each serving the viewers near it. That illustrative one-and-a-half terabits for one region becomes fifteen-to-seventeen appliances working in parallel inside that region, each pushing its ninety-to-a-hundred gigabits to the viewers on its doorstep — instead of one impossible origin pipe.
The origin does not disappear. Its job changes completely: it stops being in the viewer's path. Instead it fills the edge — it pushes copies of the videos out to the PoPs ahead of time, during off-peak hours — and it answers the rare miss, when a PoP is asked for a segment it does not happen to have cached yet: it fetches that one segment from the origin once, serves it, and keeps a copy for the next viewer. In steady state the overwhelming majority of bytes never touch the origin at all, and the hottest titles are served straight from each appliance's memory. The aha is that egress does not scale up; it scales out and outward — out across many appliances, and outward toward the viewer until the bytes are being served from inside the viewer's own network. The right question was never how do I push more bytes from here; it was how do I stop pushing them from here at all.
Under the hood — how it works
The edge tier's throughput job is the whole point, and it sizes from the bytes up: take the region's peak outbound demand and divide by what one appliance can push. The illustrative one-and-a-half terabits a second divided by ninety-to-a-hundred gigabits an appliance is about fifteen-to-seventeen appliances for that region, and the global fleet is that math repeated everywhere the audience is — in practice thousands of appliances across dozens of internet exchange points. It is tempting to picture the origin as a still-busy hub with a cache bolted on, quietly serving the popular stuff; that picture is wrong, and the difference matters for sizing. Because the origin is off the routine hit path, its outbound rate is a small fraction of the total — which is exactly what makes a fleet of ninety-to-a-hundred-gigabit appliances, rather than a giant origin, the answer to a terabit wall.
The edge's capacity per PoP is a working set, not the whole catalog: a storage-dense appliance holds a few hundred terabytes, enough for the popular slice its region actually watches, with the hottest ten to twenty percent in RAM. A PoP does not need the whole library, because the long tail falls through to the origin as a miss. Its failure story is gentle by design: if an appliance dies, its viewers re-route to a sibling PoP or, worst case, briefly to the origin, and a cache miss is just a one-time fetch-and-fill. The edge holds no unique durable state — everything it serves is a copy of what the origin holds — so losing an appliance costs some cache warmth, never any actual video. The one failure that is not gentle, a whole region losing its control plane, is the last use case, and the edge is exactly what makes that survivable.
⚡ From production
A single Netflix Open Connect appliance sustains roughly ninety to a hundred gigabits per second of TLS-encrypted video from one box, reached by tuning the kernel network stack rather than only adding hardware, with the hottest ten to twenty percent of its titles served straight from RAM. Netflix has deployed more than 8k of these appliances to over a thousand ISPs across dozens of internet exchange points, at over a billion dollars of its own investment. That this is every day's baseline, not a hypothetical, shows up in independent measurement: Sandvine found Netflix and YouTube together account for roughly 28% of all global downstream internet traffic.
The board after this deep dive — “A viewer watches” traced, hop by hop. The numbered steps below walk the same path.
A viewer watches
the player pulls the next segment from the nearest PoP — a short hop away, a cache hit in a few milliseconds~3 ms
only on a miss: the PoP fetches that one segment from the origin once, serves it, and keeps a copy for the next viewertens of ms
separately and off-peak, the origin proactively fills the PoPs with copies of the videos, so in steady state the bytes never touch the origin~1 ms
use case 2 What does the edge cache, and how does a shaky-network viewer stream it?
Now two answers the edge tier deferred come due, and they turn out to be one answer. What does the edge actually store, and what does the player actually request? And how does someone on a flaky, shifting connection still get smooth playback? Both fall out of a single choice about how a video is encoded and sliced — which, along the way, makes the storage bill far smaller than anyone expects.
deep dive 2 What does the edge cache, and how does a shaky-network viewer stream it?
The question
The edge serves bytes close to the viewer. But two questions the last use case set aside now have to be answered together. What exactly are those bytes — what unit does the edge cache and the player fetch? And how does a viewer on a weak, changing connection get a stream that stays smooth, when the single-server version could only offer one stored quality? A connection is strong at home, weak on a phone, and it changes minute to minute on the move. The service has one job: keep the video playing smoothly the whole time, sharp when the connection is good and never frozen when it is bad, without the viewer touching a quality setting.
Start with what breaks if you serve one fixed quality — you lose either way. Pick a high bitrate and the phone viewer stalls constantly, because their connection cannot pull the bytes fast enough. Pick a low bitrate and the fiber viewer gets a blurry stream on a connection that could have carried far better. And a viewer whose connection changes mid-video cannot be served correctly by any single fixed choice, because the right answer changed while they were watching. So the video has to exist at several qualities, and something has to get each viewer the right one — which the client-pull ladder does without any server-side per-viewer decision.
That leaves the choice of the ladder itself: how many rungs, at what bitrates, and cut how. The baseline is a fixed ladder — one set of bitrates applied to every title, the same rungs whether it is a dark, slow drama or a bright, fast sports clip. It is simple and it works, and it was the industry norm for years, with a top rung around 5.8k kilobits for 1080p. But it spends bits badly: a simple-to-compress title does not need the top rung's full bitrate to look identical, and a fixed ladder has no way to know that. The better default is a per-title, content-aware ladder: analyze each title and choose its rungs for its actual content, so an easy title gets a lower top-rung bitrate at the same perceived quality. On a real title that trimmed the top rung by about 20% for equal quality — bytes saved on every rung, on every segment, for every one of the millions of views. At this egress scale, a fifth off the bitrate is enormous.
A per-title, content-aware ABR ladder of renditions, cut into ~6-second segments, with a client-pull player. One quality is not enough; the video has to exist at several. This is the ABR ladder — ABR for adaptive bitrate. The source video is encoded into a set of renditions, each a full copy of the video at a different quality and bitrate: a top rung at high bitrate for strong connections, a few middle rungs, and a low rung at a few hundred kilobits for the worst connections. Each rendition is cut into short segments, a few seconds each, and the current authoring guidance puts a segment at about six seconds. The whole ladder — every rendition, cut into segments — plus a small manifest listing what exists is what actually gets stored and served.
Now the half that kills the most common myth. The instinct is that the server watches your connection and decides what quality to push you. That is not how it works. The player pulls; the server does not push. When playback starts, the player first fetches the manifest — the small file that lists every rung and where each rendition's segments live. Then the player fetches the video one segment at a time, and before each segment it chooses the rung: it looks at how fast the last few segments actually arrived and how much buffer it has, and it requests the next six-second segment at the highest rung its own measured throughput can sustain. Connection drops on that walk outside? The next segment the player asks for is a lower rung, and playback stays smooth at reduced quality instead of freezing. Connection recovers? The player steps back up. The server has no per-viewer quality decision to make — it just serves whatever segment is asked for, which is exactly why those fetches cache so well at the edge.
Here is where the surprise from the brief pays off, and it is the most important number on the board. Now that you know what is stored — the whole ladder of renditions, cut into segments — size the library from the bytes up. One source hour at the old top rung is about two and a half gigabytes. A full ladder runs several more rungs below that, so budget very roughly twice the top rung for the whole set, call it about five gigabytes per source hour. A region's catalog is about eight thousand titles at a blended runtime of maybe an hour and a half each. Multiply it out and it is about sixty terabytes — and even generously multiplying by ten for extra audio languages, subtitle tracks, and a global footprint, the whole thing is still well under a petabyte. The exact same bytes, served to a huge audience, were terabits per second of egress in the last use case — the wall — while the bytes at rest are tens of terabytes. The scary number was always the small one.
Under the hood — how it works
The player has to learn what rungs exist and where the segments live before it can fetch anything, and that information is not the video bytes. It is small structured data: for each title, its list of renditions and the manifest that points at their segments. That lives in its own component, the Metadata catalog — a read-mostly index the player hits first, before it ever touches the edge. It is tiny compared to the video: kilobytes of pointers and manifests per title, not the gigabytes of segments. And it is read enormously — one manifest read to start every single playback — so it is served from read replicas and tuned for reads. The catalog holds the map; the edge holds the bytes; they are never confused for each other.
The ladder's throughput is the cheap part: the player requests one six-second segment at a time at its sustainable rung, and each of those requests is an ordinary edge cache hit. Its capacity is the number just computed — tens of terabytes a region, low petabytes globally — and that capacity is what the durable store behind the edge has to hold. Its failure story is soft: if a rung is momentarily unavailable, the player simply fetches a lower one it can get, because adapting down is already the normal behavior. The Metadata catalog's throughput is dominated by manifest reads, one per playback start, served from replicas; it writes only when a new rendition is published. Its capacity is small, and its failure story is gentle and AP-flavored: a replica loss shifts reads, and a slightly stale manifest still gives the player a working list of rungs, so playback is unaffected. The heavy, durable bytes are not here; they are in the store the next use case builds.
⚡ From production
Apple's current HLS authoring specification targets a six-second segment, down from an earlier ten-second guidance. Netflix's earlier fixed ladder topped out at 5.8k kilobits for 1080p, with rungs at 2.4k and 1.8k below it; moving to per-title, content-aware encoding cut the top-rung bitrate by about 20% on a real title at equal or better perceived quality. And the client-pull model is the standard: the player, not the server, chooses each segment's rung from its own measured throughput.
The board after this deep dive — “A viewer watches” traced, hop by hop. The numbered steps below walk the same path.
A viewer watches
the player first reads the manifest from the metadata catalog — the small file listing every rung and where its segments live~5 ms
the catalog returns the rung list, served from a read replica~2 ms
then the player pulls the next segment from the nearest PoP, at the highest rung its own measured throughput can sustain — an edge cache hit~3 ms
only on a miss: the PoP fetches that one segment from the origin once, serves it, and keeps a copy for the next viewertens of ms
separately and off-peak, the origin proactively fills the PoPs with copies of the videos, so in steady state the bytes never touch the origin~1 ms
use case 3 Do not block the upload, and do not lose it
Playback is fully handled now. But the ladder those players climb has to be produced by something, and the creator's original file — often a full gigabyte — has to arrive intact before any of that can start. On one box, done inline, this small pipeline snaps in two separate places once real creators show up.
deep dive 3 Do not block the upload, and do not lose it
The question
The read path is built: the edge serves bytes near the viewer, and the player climbs a ladder of renditions. But that ladder does not appear by magic. Something has to turn one creator's source video into all those renditions, and something has to get the gigabyte source file in safely in the first place. Creators upload constantly — hundreds of hours of new video every minute across the service. Each source video has to be encoded into its whole ABR ladder before anyone can watch it, and encoding is heavy. Two things cannot happen: the creator cannot wait for the encode to finish before their upload is accepted, and a gigabyte upload cannot be lost to a dropped connection.
The arrival rate itself is the argument for the shape. Encoding on upload is the instinct, and it dies on a number: about five hundred source-hours a minute, each fanned into a ladder of maybe six rungs, is roughly three thousand encoded hour-equivalents a minute. No fixed set of machines bolted to the upload path could clear that inline, and every creator would be left watching a progress bar for the full encode. Work that arrives far faster than it can be done immediately, that must not be lost and can be done slightly later, is the textbook case for a durable job queue and a worker pool: the upload enqueues a job and returns, and the fleet drains the queue as fast as its worker count allows.
The point of a durable job queue is that the job lives in the queue, not in the worker. A worker leases a chunk-encode job, does it, and marks it done; if the worker dies mid-encode, the lease expires and the job goes back to the queue for another worker to pick up. The creator already got their success — the source video is safe in the blob store — so a lost worker only delays that video's renditions slightly, it never loses the video. This is the same self-correcting property a partitioned job queue gives any independent-task pipeline, which is exactly the shape here: the encode jobs are independent of each other, so they fan across a partitioned queue and a worker pool cleanly.
Accept resumable chunks, return on durable store, and encode the ABR ladder async through a job queue and a parallel worker fleet. Take the encoding first, because a number settles it. The instinct is to encode on upload — the creator uploads, the server transcodes into the renditions right then, and returns success when they are ready. At one upload an hour, fine. At the real rate, impossible: roughly five hundred hours of source video arrive every minute, and each hour has to be encoded into a ladder of maybe six rungs, which is about three thousand encoded hour-equivalents of work every minute. No synchronous per-upload step can swallow that. Work arriving far faster than it can be done immediately, that must not be lost and can be done slightly later, is exactly what a durable job queue with a worker pool is for. In practice that queue is an SQS- or Kafka-class durable log. So the upload does not trigger an encode. It enqueues an encode job.
The upload path's only synchronous responsibility is to get the source bytes stored durably and hand the creator a success — the video is safe, it will be ready to watch shortly. Behind the queue, a worker fleet pulls jobs and encodes, and it scales horizontally: more encode work simply means more workers draining the same queue. One more move makes it fast instead of merely unblocked — a single source video does not have to be one long encode on one worker. It is split into many short chunks, each chunk encoded in parallel by a different worker, then the encoded chunks are joined back into the finished renditions. Netflix's own pipeline fans one upload into roughly thirty-one parallel chunk-encode jobs this way. The encode itself — the codec work of turning a chunk of pixels into a rung's bitrate — is treated as an opaque job the worker runs; this design sizes and schedules that work, it does not re-derive the codec.
Now the upload transport, the second failure. A source video is gigabytes, and a plain single upload that drops at ninety percent has to start over from zero, which at gigabyte sizes is brutal and common. The fix is resumable chunked upload: the file is sent in chunks over HTTP, the server tracks how far it got, and a dropped connection resumes from the last received byte rather than restarting. This is a solved, standardized thing — the tus protocol is the open standard behind several major services' resumable uploads — so the design adopts it and moves on. The upload service that accepts these chunks is stateless in front of the durable store: it buffers the incoming chunks, and the moment the full source video is durably stored it returns success and drops an encode job on the queue. Success means your bytes are safe, not your video is encoded.
Under the hood — how it works
The Upload service is sized by the upload arrival rate, is stateless, and scales horizontally behind a load balancer; it holds only in-flight chunk buffers, so its own capacity is near-zero and the durable bytes land in the blob store. A dropped upload resumes from the last byte; a dead upload worker loses only an in-flight buffer, re-sent on resume. The Transcode pipeline is sized by the encoded-output rate — those roughly three thousand hour-equivalents a minute set the worker count, and cost tracks per normalized minute of output — while the queue itself holds only pending jobs, bursty and drained by the fleet. A dead encode worker's job is re-leased and retried.
The Blob store is the durable one, and its capacity is the number from the last use case: tens of terabytes a region, low petabytes globally, for the whole encoded ladder plus the source masters. Its throughput is one write per encoded rendition out of the pipeline, and it is not in the steady-state playback path — the edge is. Its failure story is the serious one on the board: a lost shard is lost renditions, so it is replicated and backed up. The durability of the entire system lives here, because the edge and the catalog can both be rebuilt from it — the edge re-fills, the catalog's manifests re-derive — but nothing rebuilds the blob store.
⚡ From production
Netflix's Video Encoding Service splits one source video into roughly 31 chunks and encodes them in parallel through an orchestration DAG — a graph of dependent encode steps — rather than encoding a title as one long serial pass; AWS MediaConvert bills that transcoding per normalized minute of output, which is how the worker fleet's cost is sized. And resumable chunked upload is a standardized protocol — the tus protocol sends a large upload in chunks over HTTP and resumes a broken upload from its last received byte, the open standard behind Cloudflare Stream's, Vimeo's, and Supabase's resumable uploads.
The board after this deep dive — “A creator's upload lands” traced, hop by hop. The numbered steps below walk the same path.
A creator's upload lands
the creator sends the source video in resumable chunks, resuming from the last received byte if the connection drops~5 ms
the moment the full source is durably stored in the blob store, the upload service returns success — the creator is unblocked here~10 ms
separately, the upload service drops an encode job on the transcode pipeline's durable queue~1 ms
workers pull the job, fan the video into ~31 parallel chunks, encode the whole ABR ladder, and write the renditions back to the blob store~1 ms
and record each rendition's manifest entry in the metadata catalog, so the player can find it~1 ms
use case 4 When the region goes dark
For the happy path, the whole thing is finished. This last use case starts it already in failure — and the shock is where the failure is. Not in anything carrying video bytes, but in a quiet management layer to one side, which still manages to pull streaming down: exactly the outage the edge tier was supposed to rule out.
deep dive 4 When the region goes dark
The question
Step back and look at what is built. It is complete for the happy path: the edge serves bytes near every viewer, the player climbs a ladder that fits its connection, the catalog hands out manifests, and uploads are accepted fast and encoded off to the side. Now the premise arrives already broken, the way a real incident does. On one Christmas Eve, one of the biggest streaming services in the world went dark for about seven hours across the United States, Canada, and Latin America. The cause was not a flood of viewers and not a failure of any serving box. An internal maintenance process was run by mistake against production state for a slice of a cloud provider's load-balancing fleet — the control plane that directs traffic to servers — and it deleted that state. The video bytes were fine. The stored renditions were fine. But the layer that routes a viewer's request to a server was gone in that region, so requests could not be steered to where the bytes lived, and televisions across three continents showed an error instead of a movie.
The instinct is to read this as bad luck with one cloud provider, or an argument for more redundancy in that provider's load balancers. Both miss the lesson, and the lesson is the whole reason the edge tier is shaped the way it is. A more redundant control plane is still a control plane playback depends on in real time — make it five-nines and a bad-enough maintenance slip still takes it, and every stream with it. The engineering answer is not to make the central dependency stronger; it is to remove the dependency from the hot path. The bytes a viewer needs are already at the edge, a short hop away, so playback can ride the nearest cache instead of the farthest control plane.
The honest counterpoint belongs in the lesson, because it is the other half of what this incident teaches. The edge decouples playback, but the incident happened because a single region's control plane was a hard dependency in the first place. The buffer only helps if the design actually leans on it — if the edge is allowed to serve without checking in, and if the control plane itself is not a single regional point of failure that a maintenance slip can delete wholesale. During a control-plane failure, a PoP that already holds a title keeps serving it outright; a PoP that gets a miss cannot fill from a dead region, so that one obscure title degrades — but that is a soft edge, not a hard floor. The popular titles the region is actually watching are already cached, often in RAM, so the overwhelming majority of playback continues while the long tail waits for the control plane to come back. Degrading the rare miss while protecting the common hit is exactly the shape graceful should take.
The edge is the availability buffer — decouple playback, degrade to stale, fail the control plane not the stream. Ask why a deleted control plane could stop playback at all. It is because playback in that moment still depended, in real time, on a central regional control plane to connect viewers to bytes. And that is exactly the coupling the edge tier is built to break. The bytes a viewer needs are already sitting in a cache inside their own internet provider, a short hop away. If playback is decoupled from the origin and the central control plane — if the edge can keep serving the segments it already holds without phoning home for permission on every request — then a regional control plane can disappear and viewers already watching keep watching, because their bytes never needed the control plane in the first place.
This is where the signature dive pays off a second time, reframed. The edge tier was introduced to beat the egress wall — to spread the bytes across appliances near the viewer. Its second, quieter job is exactly this: because the bytes live at the edge, close to the viewer and independent of any one central control plane, the edge is what keeps playback alive when the center fails. Graceful degradation here means the edge keeps serving the segments it already has, even if the catalog is briefly unreachable and the manifest a player holds is a little stale, rather than failing every stream the instant the control plane hiccups. A viewer mid-movie should not care that a management layer three hops away just went down. The reframe is bigger than this board: a system that serves bytes to the world should push the thing viewers depend on — the bytes — as close to them and as far from a single central dependency as it can, and then let the far management layers fail without taking the near bytes with them.
Under the hood — how it works
Graceful degradation here is a policy on how the edge serves, not a component, which is why it is a badge and not a box. When the catalog and the central control plane are briefly unreachable, a PoP keeps serving the segments it already holds and honors the manifest a player already has, even if that manifest is a little stale. The one thing it cannot do is fill a genuine miss from a dead region — so a title the region was not already watching may fail to start until the control plane returns. That is the trade: the rare miss degrades, the common hit is protected, and no viewer mid-stream is dropped for a management-layer outage three hops away.
The reframe carries past this board. The Christmas Eve outage was a single region's control plane deleted wholesale, and the fix has two halves: make playback ride the nearest cache rather than a far central control plane, and do not let that control plane be a single regional point of failure a maintenance slip can wipe. The first half is what the edge tier already gives you for free — the bytes are near the viewer — as long as the design is actually willing to serve them without phoning home on every request. That willingness is the whole rescue.
⚡ From production
Netflix's Christmas Eve outage was caused by an internal maintenance process that inadvertently ran against production state data for a portion of AWS's Elastic Load Balancer fleet and deleted it. TV-connected devices and game consoles across the US, Canada, and Latin America lost streaming for roughly seven hours, until the affected load balancers were restored; the UK, Ireland, the Nordics, and the website itself were unaffected, because the failure was a single region's control plane, not the video serving itself. It is the clearest case for why playback should ride the edge and not a central control plane.
The board after this deep dive — “A viewer watches” traced, hop by hop. The numbered steps below walk the same path.
A viewer watches
the player first reads the manifest from the metadata catalog — the small file listing every rung and where its segments live~5 ms
the catalog returns the rung list, served from a read replica~2 ms
the player pulls the next segment from the nearest PoP — and when the catalog and central control plane are briefly unreachable, the PoP keeps serving the segments it already holds against a slightly stale manifest, so playback does not go dark~3 ms
only on a miss: the PoP fetches that one segment from the origin once, serves it, and keeps a copy for the next viewertens of ms
separately and off-peak, the origin proactively fills the PoPs with copies of the videos, so in steady state the bytes never touch the origin~1 ms
Where this design sits on consistency (and CAP)
This design makes opposite durability choices for its two kinds of state, on purpose, because they are worth completely different things. The playback pipeline — the CDN edge tier, the metadata catalog, and the segments a viewer pulls — chooses availability and throughput over strong consistency. The edge serves the segments it already holds, close to the viewer; the catalog is read-mostly and answered from whatever replica is near; a segment that drops is simply requested again; and a manifest that is a little stale still hands the player a working list of rungs. None of this coordinates to be instantaneously correct, because a stream is smooth-and-available, not transactional.
The Blob store makes the other choice. The encoded renditions are the product — the thing the edge fills from and the thing everything else rebuilds from — so a lost shard is a real, unrecoverable harm, not one the next request repairs. So the Blob store is the durability-leaning dataset: it lives in a replicated, backed-up object store, and it is the one place the system's work is safe. That is exactly why the edge and the catalog can afford to be cheap and rebuildable — the edge re-fills from the store, and the catalog's manifests re-derive from it. Durability is bought where it matters and declined where it does not.
There is a name for the underlying choice. When a network fault splits a system in two — that split is the P, for partition — each operation has exactly two honest options: answer anyway from what this half knows and risk being wrong, which is the A, for availability, or refuse until the halves agree, which is the C, for consistency. A partition is not something you choose; it is weather. The useful way to apply the CAP theorem is per operation, by asking what a wrong answer would cost. Here a slightly stale manifest or a re-requested segment costs almost nothing while a lost archive of renditions costs everything, so the design takes availability and throughput for the playback pipeline and pays for durability only on the Blob store. The Christmas Eve outage is the same lesson in the field: playback should ride the nearby edge, not a far central control plane.
That is the whole machine, two paths that meet at the durable store, and every box on it was placed by one of the deep dives above. On the read path a viewer's player reads the manifest from the metadata catalog, then pulls six-second segments from the nearest edge PoP, climbing and descending a ladder to match its own connection; the origin — now the blob store — is off the hot path entirely, filling the edge off-peak and answering only the rare miss. On the write path an upload service accepts the source in resumable chunks and returns the moment the bytes are safe, then a transcode pipeline fans the video into parallel chunks, encodes the whole ladder, writes the renditions to the blob store, and records their manifests in the catalog. The edge serves the terabits, spread across appliances near the viewers; the blob store holds the tens of terabytes durably; the catalog holds the small map; and when a region's control plane fails, the edge keeps serving what it holds. If you carry away one sentence, make it the one the whole design turned on: the scary-sounding resource is often not the wall — do the arithmetic before you design.
The finished design — every box placed by one of the deep dives above.
What's on the diagram
Creator
Creator. The person who uploads a source video, handed to the write path once per video. It is not on any hot path — the upload is accepted in resumable chunks and acknowledged the moment the bytes are durable, and everything heavy happens off to the side after that.
Viewer · player
Viewer · player. The app or browser tab that plays a video. The player pulls: it reads the manifest from the metadata catalog first, then fetches the stream one segment at a time from the nearest edge PoP, choosing before each segment the highest rung its own measured throughput can sustain — so quality follows the connection with no server-side per-viewer decision.
Blob store · object store
Blob store · object store. The durable system of record — an object store holding every source master and every encoded rendition, tens of terabytes a region and low petabytes globally, replicated and backed up. It is off the steady-state playback path: it fills the edge off-peak and answers only misses. It is the one box whose loss is unrecoverable, because the edge re-fills from it and the catalog's manifests re-derive from it, but nothing rebuilds it. It was born as the all-purpose Origin and relabeled once its content became the durable renditions.
CDN edge tier · PoPs
CDN edge tier · PoPs. The signature: a tier of cache appliances installed deep in the network, close to the viewer and often inside their own internet provider, each sustaining roughly ninety to a hundred gigabits a second with the hottest titles served straight from RAM. It serves the terabits of egress, spread across the fleet, so no single origin uplink is the ceiling; the origin only fills it off-peak and answers the rare miss. Because the bytes live here near the viewer, it is also the availability buffer — when a region's control plane is unreachable, it keeps serving the segments it already holds and degrades only the rare uncached miss, rather than failing every stream.
Metadata catalog · Postgres
Metadata catalog · Postgres. The read-mostly index the player reads first — each title's list of renditions and the per-title manifest that points at their segments, kilobytes per title, not the gigabytes of video. It is read enormously (one manifest read to start every playback) so it is served from read replicas and tuned for reads, and written only when a new rendition is published. It holds the map; the edge holds the bytes; a stale manifest is tolerable (a replica loss shifts reads, playback is unaffected).
Upload service · tus
Upload service · tus. Stateless intake that accepts the source video in resumable chunks over HTTP, resuming from the last received byte when a connection drops, and returns success the moment the source is durably stored — not when it is encoded. It holds only in-flight chunk buffers, so a dead upload worker loses only a buffer that is re-sent on resume, and it scales horizontally behind a load balancer. The moment the source is durable it drops an encode job on the transcode pipeline.
Transcode pipeline · queue + workers
Transcode pipeline · queue + workers. A durable job queue and a parallel chunk-encode worker fleet that turns one source video into its whole ABR ladder off the upload path. It fans each upload into roughly thirty-one chunks encoded in parallel, writes the finished renditions back to the blob store, and records each rendition's manifest entry in the catalog. The queue owns the work, so a dead worker's job is simply re-leased — the creator already has their success. Sized by the encoded-output rate; the codec itself is treated as an opaque job.
What this design never covered — raise it yourself
The encoding algorithm itself
A strong follow-up is how the codec actually compresses each frame — H.264, HEVC, AV1, per-scene rate control. The honest answer names it as the opaque job this board sized but did not re-derive: the pipeline decides how much to encode and schedules it, but the codec is its own deep problem.
Live streaming
The sharpest what-about is broadcasting an event as it happens — sub-second ingest, no chance to pre-encode, a different chunking economics. The strong answer names it as a distinct problem with its own forcing numbers, not a variation on this VOD board.
Content protection
A follow-up on stopping a paid video from being copied points at license servers and key exchange. Name it as a rights-and-security layer bolted onto playback that no systems number here forces, rather than folding it into the delivery spine.
What to recommend and what to autoplay
The deepest what-is-this-for is which video a viewer sees next — the home screen, the ranking. Name it as a separate personalization and machine-learning system that decides which video to request; this board plays whatever video is asked for.
Cache-fill strategy and the long tail
A sharp interviewer may ask how the edge decides what to pre-fill and what to leave for a miss, and what a cold long-tail title costs. Name it as a real cache-strategy question — push the popular titles proactively off-peak, pull the long tail through on a miss — that this board treated as the edge's fill policy rather than a sourced design number.
Go deeper — the primitives this design leaned on
Durable job queue with a worker pool ↗ — producers enqueue independent jobs onto a durable, partitioned log and a group of workers drains them in parallel, offsets tracking progress per partition; it is exactly the transcode pipeline, uploads enqueuing encode jobs a worker fleet processes in parallel