Netflix streams a catalogue of thousands of licensed films and series to more than 300 million paid memberships, mostly on televisions and mostly in the same few evening hours in each time zone. Unlike a platform built on user uploads, it knows almost everything in advance: which titles exist, when the next season lands, and roughly how many people in each city will watch it tonight. That foresight changes the whole design. The hard parts are putting the right files inside every ISP before anyone presses play, squeezing the best possible picture out of every bit, sending each play to a healthy server that already has the file, and keeping the cloud brain that coordinates all of it running when an entire region fails.
Understand the problem
The interviewer says “design Netflix” and many candidates draw YouTube: uploads, transcoding queues, a pull-through CDN. That misses the point. Netflix has no user uploads, a catalogue that is tiny by internet standards, and viewers who expect television quality on a television. Say the difference first: the content is known in advance, so we can push it to the edge before demand, and we can afford to spend lots of compute encoding each title well.
Functional requirements
- Members should be able to browse a personalised homepage — rows of titles with artwork — and search the catalogue available in their country.
- Members should be able to press play and watch at the best quality their device and network allow, with audio and subtitles in their language.
- Members should be able to resume where they left off on any device, and see a “continue watching” row.
- Content operations should be able to publish a licensed title — video, many audio tracks and subtitles — so it becomes playable in exactly the licensed countries at a scheduled launch time.
Non-functional requirements
- Fast, stall-free playback. Video starts in under 1.5 s at p50 and under 4 s at p99; fewer than 1 in 200 plays should ever rebuffer. On a 65-inch television, a stall or a blocky frame is far more visible than on a phone.
- High availability. 99.99% for browsing and starting plays — the internal goal Netflix described when it moved to active-active regions — and streams already playing must survive a control-plane outage.
- Scale. About 300 million memberships (301.6 million at the end of 2024), on the order of 35–40 million concurrent streams at the global evening peak, and about 150 Tbps of video (derived below).
- Licensing is never wrong. A title must not play in a country where it is not licensed, or before its launch time. Entitlement checks fail closed.
- Quality per bit. Members in places with expensive or slow bandwidth should still get a sharp picture, so spend compute offline to save bits online.
- Consistency chosen per data type. Licences and entitlements are strongly consistent at the source; resume positions and homepage rows can be seconds stale; viewing history favours availability.
In the interview
Ask: “Is the catalogue licensed and known in advance, or user-uploaded?”, “Which devices matter most — TVs, phones, both?”, “Do we run our own CDN or rent one?”, and “Is live streaming in scope?” The first answer is what lets you say pre-position instead of cache on demand, and it shapes every later choice.
Out of scope
- The recommendation models themselves. We consume precomputed rows; how they are ranked belongs to design a recommendation feed.
- Billing, sign-up and payments. Standard subscription services; see design a payment system.
- Studio production and content acquisition. We start when a licensed master file arrives.
- Live events. A different pipeline; covered briefly in the follow-ups.
- DRM internals. We call a licence service and treat the cryptography as given.
The set-up
Plan the approach
Draw the line between the control plane and the data plane in the first five minutes. The control plane is everything that decides — homepage, licences, which files, which servers, where you stopped — and it handles thousands of requests a second. The data plane moves bytes, about 150 Tbps of them, and must never wait on the control plane once a stream has started. Then build each requirement, and spend the deep dives on pre-positioning, steering, encoding, resilience, viewing history and launch night.
Core entities
- Title. A film or a series with seasons and episodes; runtime, maturity rating, artwork set, synopses in many languages.
- Rights window. Title × country × start and end time. The single most important row for “may this play here, now?”.
- Stream file. One encoded asset: video at a given codec, resolution and bitrate; an audio track in a language and format; or a subtitle track. A title has well over a hundred of them.
- Profile and viewing record. Who watched which episode, up to which second, on which device, when.
- Appliance (OCA). A cache server in an ISP or an exchange: its site, cluster, the files on its disks, its health and the routes it has learned.
API
Homepage. Precomputed rows, filtered to titles licensed in the member’s country, with one chosen image per title.
GET /v1/home?profile=p7&rows=40
→ 200
{
"rows": [
{ "title": "Continue watching", "items": [ { "id": "t812", "episode": "s2e4", "progress": 0.52, "art": "img/t812/a3.webp" } ] },
{ "title": "Indian crime thrillers", "items": [ { "id": "t455", "art": "img/t455/a1.webp" }, "…" ] }
]
}
Start a play. The device states what it can decode; the playback API answers with the files to use and a ranked list of appliances for each.
POST /v1/playback/start
{
"profile": "p7", "title": "t812", "episode": "s2e4",
"device": { "codecs": ["av1", "hevc", "avc"], "max_height": 2160, "hdr": true, "drm": "widevine-l1" },
"audio_lang": "hi", "subtitle_lang": "en"
}
{
"play_id": "pl_9f31",
"video": [ { "file": "f_av1_1080_2350", "kbps": 2350, "height": 1080 }, "… the per-title ladder …" ],
"audio": { "file": "f_aud_hi_51", "kbps": 448 },
"subtitles": { "file": "f_sub_en_ttml" },
"servers": [
"https://oca-a.isp-pune.example.net/f/…?exp=…&sig=…",
"https://oca-b.isp-pune.example.net/f/…?exp=…&sig=…",
"https://oca-c.ix-mumbai.example.net/f/…?exp=…&sig=…"
],
"drm_licence_url": "/v1/drm/licence?play=pl_9f31"
}
Resume. The player reports progress; any device can read it back.
PUT /v1/profiles/p7/progress { "title": "t812", "episode": "s2e4", "position_s": 1880, "device": "tv-lr", "ts": "…" } → 204
GET /v1/profiles/p7/continue-watching → 200 [ … ]
Appliance ↔ control plane (internal). Appliances report state and ask what to store; they never talk to members’ accounts.
POST /oc/v1/report { "oca": "oca-a", "health": { … }, "bgp_routes": [ … ], "files": [ … ] }
GET /oc/v1/fill-plan?oca=oca-a → 200 { "add": [ … ], "delete": [ … ], "sources": { "f_av1_1080_2350": ["oca-b", "ix-mumbai-3", "s3"] } }
Back-of-envelope estimates
Show the arithmetic. (Method: back-of-envelope estimation.)
Memberships. Netflix reported 301.6M paid memberships at the end of 2024 — its last quarterly figure, since it stopped reporting the number each quarter from 2025. Use 300M.
Viewing. Assume one hour per membership per day: 300M hours a day. (Sanity check: in January 2018 Netflix described members watching over 140M hours a day, when it had over 100M members.)
Concurrent streams. 300M h ÷ 24 = 12.5M streams on average. Viewing bunches into evenings, so assume the global peak is 3× the average: about 37.5M concurrent streams.
Egress. Televisions at 1080p and 4K pull the average up; phones and efficient encodes pull it down. Assume 4 Mbps: 37.5M × 4 Mbps = 150 Tbps at peak.
Appliances. Netflix described a single appliance serving about 90 Gbps of encrypted traffic back in 2017. Plan for 50 Gbps per box at peak, leaving room for failover and fills: 150 Tbps ÷ 50 Gbps = 3,000 appliances at minimum. The real fleet is larger, because thousands of sites are each sized for their own local peak plus a spare.
The catalogue. Assume about 8,000 titles averaging 6 hours each (a film is 2 hours, a long series 50): about 50,000 hours of content.
Bytes per hour of content. Video ladders in three codec families plus device-capped ladders sum to roughly 50 Mbps; assume audio in 20 languages × 3 formats at an average 0.2 Mbps, which adds 12 Mbps. 62 Mbps × 3,600 s ÷ 8 ≈ 28 GB per hour of content. The whole playable catalogue: 50,000 h × 28 GB ≈ 1.4 PB.
A new season. 8 one-hour episodes × 28 GB = 224 GB. Netflix’s deployment guide gives a storage appliance a 12-hour nightly fill window at up to 1.2 Gbps: 1.2 Gbps × 43,200 s = 51,840 Gb ≈ 6.5 TB a night. A full copy of the new season is about 3.4% of one night’s budget.
Control-plane traffic. Assume an average play lasts 45 minutes: 300M h × 60 ÷ 45 = 400M plays a day ≈ 4.6k/s, about 14k play starts/s at peak. Assume 3 homepage loads per membership a day: 900M a day ≈ 10.4k/s, about 31k/s at peak — but each page shows dozens of titles, and Netflix has said its artwork selection alone peaks at over 20M requests a second.
Viewing history. If each stream reports its position every 60 s: 37.5M ÷ 60 ≈ 625k writes/s at peak. Netflix reports roughly 9 writes for every read on this data.
Tip
Compare the two halves. The data plane moves 150 Tbps; the control plane handles about 14k play starts a second. That ratio is why the bytes live in thousands of boxes inside ISPs while the decisions live in a few cloud regions — and why a cloud outage must not stop streams already playing. And because the whole catalogue is about 1.4 PB, a site can hold most of what its region watches, which a user-upload platform adding petabytes a day never could.
High-level design
Members should be able to browse a personalised homepage
The homepage is assembled, not computed, at request time. Overnight and through the day, recommendation jobs write precomputed rows per profile — “Because you watched…”, “Top 10 in India today”, genre rows. When the TV app opens, the request passes an API gateway (Netflix’s is Zuul) to a homepage service, which reads the profile’s rows, drops titles not licensed in the member’s country using a catalogue cache, and asks an artwork selector which single image to show for each title. Netflix has written that artwork selection is personalised per member — someone who watches romances and someone who watches comedies may see different stills for the same film — and that it peaks at over 20 million requests a second, so in our design it runs from in-memory models, with a non-personalised default image ready if it is slow.
Search is a separate service over an index of titles, people and genres in dozens of languages, filtered by the same country catalogue. It is small — tens of thousands of titles — so it is a latency problem, not a scale problem.
Members should be able to press play and watch
This path crosses from the control plane into the data plane, and the boundary is the whole point. The playback API in the cloud checks the member’s plan and the title’s rights in their country, picks the files for this device — codec, resolution cap, HDR, audio language, subtitles — and asks a steering service which appliances should serve them. Steering knows, because every Open Connect appliance (OCA) regularly reports its health, the network routes it has learned from its ISP, and which files are on its disks. The player gets the file list, a ranked set of URLs and a DRM licence URL, then streams straight from an appliance.
This is the five-step flow Netflix’s Open Connect overview describes: appliances report state; the device asks the playback service; the service checks authorisation and licensing and chooses files for that device and network; steering picks appliances and generates URLs; the device streams. The appliances store no member data at all — no viewing history, no account details — only encrypted media files. That keeps the boxes inside ISPs simple, replaceable and harmless if one is stolen.
Rights checks deserve one more sentence. They run on every play start against the rights windows for the member’s country, and they fail closed: if the rights service cannot answer, the play does not start. A few seconds of “try again” is better than streaming a film in a country where the licence ended at midnight.
Members should be able to resume where they left off
While playing, the player reports its position every minute or so, and on pause and stop. A viewing history service writes those records to Cassandra, keyed by profile, with an in-memory cache (Netflix’s EVCache, a memcached-based tier) in front for reads. When the same profile opens the app on a phone the next morning, “continue watching” and the resume point come from the cache.
Cassandra fits because the workload is write-heavy, partitioned naturally by profile, and happy with eventual consistency: if the phone shows a position 30 seconds behind the TV, nobody minds. Deep dive five shows why the naive “one row per profile” model eventually slows down.
Content operations should be able to publish a licensed title
A studio delivers a high-quality master plus audio stems, subtitles and artwork. Ingest runs quality control — frame checks, audio sync, subtitle timing. Encoding produces the full set of stream files, usually days before launch. Everything lands in object storage (Amazon S3), and the Open Connect control plane starts filling appliances worldwide in their nightly windows. A title becomes live only when three things are true: enough appliances hold enough copies, the rights window has opened in that country, and the scheduled launch time has passed.
That is a complete Netflix: a homepage, plays steered to appliances, resume across devices, and titles that go live on schedule. The deep dives make each part hold at 150 Tbps and 300 million members.
Deep dives
How do we get every title inside the ISP before anyone presses play?
At 150 Tbps, where the bytes come from decides cost, quality and whether the internet’s interconnects survive your evening peak.
Bad: a pull-through CDN, rented. A commercial CDN caches whatever members request. It works for the steady state, but launch night is the worst case: the first viewers of a new episode miss in every edge at once, and every miss crosses the ISP’s interconnect at exactly the hour it is busiest. You also have no say over which server a member reaches or what it holds.
Good: a rented CDN, pre-warmed. Push the expected hits to the CDN’s edges before launch. Misses drop, but the servers still sit outside most ISPs, so the evening peak still flows over peering links and transit, and you pay per gigabyte for a workload whose demand you could have predicted.
Great: your own appliances inside ISPs, filled at night from a forecast. This is Netflix’s Open Connect. Netflix started it in 2011 and deploys appliances two ways: at internet exchange points, connected to ISPs over settlement-free peering, and embedded inside ISP networks — Netflix provides the servers free to qualifying ISPs, and the ISP provides power, space and connectivity and decides which of its customers are routed to them. Either way, the bytes for a family in Pune watching a thriller at 9 pm never leave their ISP. The Open Connect overview says this proactive, directed caching reduces demand on upstream network capacity “by several orders of magnitude” compared with demand-driven caching.
The trick is when the files move. An appliance serving at full speed should not also be writing new content to the same disks. So Open Connect fills during a configured window when local viewing is lowest.
Netflix’s embedded-appliance guide makes it concrete: a 12-hour fill window from 2 AM to 2 PM local time (shiftable by two hours), during which a storage appliance may pull up to 1.2 Gbps, about 6.5 TB a night. The estimates showed why that is enough: a whole new season is about 224 GB, and most nights only popularity changes and re-encodes need to move.
What goes on each box. Appliances do not decide for themselves. Each regularly asks the control plane for a manifest — the list of files it should hold — computed from a popularity feed for its content region (rankings by country or region, from past viewing) and from how many copies the cluster should keep. Files that drop out of the manifest go into a delete list and are removed later, after any session still reading them has finished.
Clusters, not copies. Where a site has more than one appliance, they act as one cluster. Netflix’s guide gives the numbers for a two-appliance cluster: both keep about 40% of their storage for the same popular content, which delivers roughly 60% of the traffic, and each uses the remaining 60% for a different slice of less popular titles. Two boxes thus hold far more distinct titles than two identical copies would, while the hottest files stay on both for redundancy and load spreading. And inside each box the very hottest files never touch disk: Netflix has written that 10–20% of the most popular titles are typically served from memory.
Where each box fills from. Pulling every file from S3 to thousands of appliances would waste time and transit. Fill is tiered.
As Netflix’s fill write-up describes it, when an appliance asks for new files the control plane returns a ranked list of fill sources per file, based on whether the source has the file, whether it can take more fill traffic, and a route cost computed from BGP paths and physical location. The order of preference is peer fill (an appliance in the same cluster or subnet), then tier fill (an appliance elsewhere in the network), then cache fill straight from S3. A few appliances per cluster are elected fill masters with permission to reach farther, sooner; they pull first and the rest copy from them. Escalation policies say how long an appliance waits before looking one tier farther. As the night moves west across time zones, the fill wave follows it. Out-of-cycle “fast track” fills exist for urgent new titles and broken encodes.
What you pay: hardware in thousands of locations, an operations team, relationships with ISPs, and the risk of a bad forecast. A surprise hit that was not pre-positioned is served from exchange-point appliances or neighbouring clusters, and gets filled into embedded boxes the next night — or sooner with a fast-track fill. With a small, predictable catalogue that is a rare, bounded failure. With 20 million user uploads a day it would be the normal case, which is why the same idea does not transfer to a user-upload platform.
How does each play find the best appliance — and survive one failing?
Having the files inside the ISP is only half the job. Each of the roughly 14,000 plays a second at peak must reach an appliance that has the right files, is healthy, has spare capacity, and is close to the member in network terms, not just on a map.
Bad: DNS-based routing. The CDN’s DNS answers with a server near the member’s DNS resolver. The resolver may be in another city; the answer knows nothing about which box holds this episode’s files; and with DNS TTLs, a failed server keeps receiving traffic for minutes.
Good: anycast or nearest-healthy-server. Routing follows network distance and health checks drop dead boxes. Better, but still blind to file placement. With the 40/60 cluster split, a member sent to the “wrong” box in a site gets a miss and a proxy fetch at peak time.
Great: per-play steering from the control plane. Every appliance reports to the control plane what files it has, whether it is healthy, and which client IP prefixes it can reach — learned over a BGP session with the ISP’s router. On each play, steering filters to appliances that hold the chosen files and have serving capacity, then ranks them using the criteria Netflix publishes for ISP partners, applied in order until one breaks the tie:
- The appliance that has the most specific route to the client’s IP prefix.
- The shortest AS path to the client.
- The lowest multi-exit discriminator (MED). Netflix adds 0 for an embedded appliance, 50 for a private interconnect and 100 for a public exchange, so an embedded box wins otherwise-equal routes.
- The geographically closest appliance, by IP geolocation.
If everything ties, traffic is balanced across the tied appliances. The member’s device receives a ranked list, typically three or more URLs, signed and short-lived. This gives ISPs a lever too: by announcing more specific routes to their embedded appliances, an ISP steers its own customers onto them, keeping traffic off its peering links.
Failover lives in the client. Netflix’s appliances serve plain HTTPS byte ranges — its engineers describe sendfile() serving range requests of around 1 MB — so any appliance holding the file can serve any range. If a request errors or arrives too slowly, the player asks the next URL for the same range. With 30 seconds of video buffered, a few hundred milliseconds of retry is invisible. Once the player has given up on URL #1, it stays on #2 for the rest of the session, and the next play gets a fresh ranking.
Steering stays honest about load. Appliances report continuously, so a box that is saturated or unhealthy drops out of new rankings within one reporting interval; plays already running on it carry on or fail over. Reports are seconds stale, so steering will occasionally hand out a box that has just degraded — exactly the case client-side failover exists for. You pay with a control plane that tracks the state of thousands of appliances and recomputes rankings for every play, in exchange for an edge that can stay simple and stateless about members.
In the interview
Name the split precisely: “Steering decides once per play, in the cloud, using routes, file placement and health. The player fails over per request, at the edge, using the ranked list it already has. Neither waits for the other, so the control plane can be briefly down without stopping a single stream that has already started.”
How do we get the best picture out of every bit?
Netflix’s economics are the reverse of a user-upload site’s. There are thousands of titles, not billions, and a hit is streamed for tens of millions of hours. Take one popular series streamed for 50M hours at 4 Mbps: a 20% bitrate saving is 0.8 Mbps × 3,600 s ÷ 8 = 360 MB per hour, × 50M hours ≈ 18 PB less traffic for that one title. Against that, even thousands of core-hours of extra encoding is a rounding error. So spend compute.
Bad: one fixed ladder for the whole catalogue. When Netflix first deployed H.264 in 2010, engineers tuned one set of bitrate–resolution pairs by eye across the catalogue, topping out at 5,800 kbps for 1080p. One recipe cannot fit everything. A simple animated show looks perfect at a fraction of 5,800 kbps, so the top rung wastes bits; grainy film still shows blocking at 5,800 kbps; and a member on a 1,750 kbps connection gets 480p for a cartoon that could have streamed in HD.
Good: a ladder per title. Encode each title at several resolutions and quality settings, measure bitrate and quality for each trial, and plot them. Every resolution has a curve that rises and then flattens; at low bitrates a lower resolution looks better (fewer pixels, more bits each), at high bitrates a higher one does. The upper envelope of all the curves is the convex hull, and the ladder should sit on it.
Netflix’s 2015 per-title write-up describes doing exactly this in production: trial encodes at quality settings one just-noticeable difference (JND) apart over a fixed set of resolutions, interpolated curves, and a ladder chosen from the points nearest the hull, with adjacent rungs spaced just under one JND so a quality switch is barely visible. The gains were large. For one animated series, 1080p started at 1,540 kbps, where the fixed ladder had served 480p at 1,750 kbps. For a drama of average complexity, the top rung dropped from 5,800 to 4,640 kbps — 20% fewer bits for the same look. Complex action titles go the other way and get a top rung above 5,800. Netflix also builds extra ladders capped at 480p and 720p, so a tablet that cannot decode 1080p gets the best ladder for it rather than the bottom of someone else’s.
This is also where adaptive streaming for Netflix is really decided. The player still chooses a rung per segment from throughput and buffer, but it chooses from a ladder already tailored to this title and this device, so each step up is about one JND of visible improvement and no rung is wasted.
Great: optimise per shot. A title is not uniformly hard: a quiet dialogue scene and a car chase in the same episode want very different bitrates. Netflix’s dynamic optimizer (2018) uses the shot — frames from one camera set-up between cuts — as the unit of encoding.
Three ideas make it work. Keyframes at shot changes, at the same timestamps in every encode — “consistently irregular”, in Netflix’s words — so no keyframe is wasted mid-shot and segments still align for switching. A perceptual metric: VMAF, which Netflix built and open-sourced, is trained on human ratings and compares encodes across resolutions, so the optimiser chases what people see rather than pixel error. Equal slopes: encode every shot at several resolutions and quality levels, then pick one point per shot such that the last bit spent buys the same quality everywhere — no shot is starved while another is overfed. Netflix reported an average 17.1% bitrate saving over the best possible whole-title fixed-quality encode (22.5% measured in PSNR), over 50% against a common two-pass VBR recipe, and similar 28–38% gains when the framework was applied to AVC, HEVC and VP9 alike.
| Fixed ladder | Per-title ladder | Per-shot optimiser | |
|---|---|---|---|
| Unit of decision | The whole catalogue | One title | One shot |
| Keyframes | Fixed interval | Fixed interval | At shot changes, aligned across encodes |
| Reported gain | Baseline | 20% at the top rung for one drama; 1080p instead of 480p near 1.5 Mbps for one cartoon | 17% over the best whole-title fixed-quality encode; over 50% over a common two-pass recipe |
| Compute | One encode per rung | Trial encodes per title, then the ladder | Trial encodes per shot × resolution × quality, then assembly |
| Fits | Huge, unpredictable catalogues | Known catalogues | Known catalogues watched at enormous scale |
What it costs. Netflix notes that each new codec generation multiplies encoder complexity by 5–10× (decoders by about 2×), and the optimiser multiplies the number of encodes again. Two things keep that affordable. Encoding is chunked: a one-hour episode becomes, say, 20 three-minute chunks encoded in parallel, and a failed cloud instance costs one chunk, not the episode. And it runs on cloud capacity that sits idle outside peak streaming hours — next week’s releases are encoded on spare machines in the small hours, when few members are streaming.
What it multiplies. Three codec families (AVC for old devices, HEVC or VP9, AV1), full and device-capped ladders, around 60 audio files and dozens of subtitle tracks: well over a hundred files per title, about 28 GB per hour of content. That is one more reason to compute fill manifests per region: an appliance only needs the codecs its region’s devices decode and the audio languages its members choose.
How does the control plane survive losing a whole cloud region?
Appliances keep serving streams that have already started, but every new play, homepage and resume needs the control plane. Netflix set itself an internal availability goal of 99.99% — under an hour of downtime a year — and a cloud region can have a bad day.
Bad: one region, with backups. Simple and cheap. A region-wide failure, or a bad deploy that breaks a critical service there, takes every member down, and restoring elsewhere takes hours.
Good: active-passive. A standby region holds replicated data and idle capacity. Better on paper, but the standby is rarely exercised, its caches are cold, and nobody is sure it can really take the full load until the day it must.
Great: active-active. Netflix’s 2013 write-up lists the rules: every service on the member’s call path is stateless; each region uses only local resources — its own storage buckets and queues; there are no cross-region calls on the call path; and data is replicated asynchronously underneath. Geo-DNS splits members between regions (then roughly 50/50 between two US regions), and either region can take everyone.
The details are where it gets real:
- Data. Member data lives in Cassandra clusters spanning regions with asynchronous, multi-directional replication. Before relying on it, Netflix wrote 1 million records in one region and read them from the other 500 ms later under production load; all were there. Good enough for profiles and resume positions; rights windows change rarely and are replicated long before they matter.
- Caches. Rather than replicate EVCache, a write in one region sends an invalidation for that key to the other region through a queue, so the next read there falls through to Cassandra and refills.
- Routing and shedding. The Zuul gateway catches requests that arrive at the wrong region, can mark a region as being in failover mode, and can cap traffic and shed the excess while a region is still scaling up or its caches are cold — protecting Cassandra from a thundering herd.
- Rehearsal. Chaos Monkey kills instances, Chaos Gorilla removes a whole availability zone, a split-brain exercise severs the link between regions, and Chaos Kong evacuates a region: in the exercise Netflix described, it moved most US traffic to US-West and kept it there for more than 24 hours.
What you pay: every region must be able to carry the members of a region it might absorb, so with two regions each runs at no more than half its capacity, and with three at no more than two-thirds. Replication is asynchronous, so in a failover a member can resume a few seconds behind where they stopped. And every service must live with those rules. See multi-region and active-active.
How do we store viewing history for hundreds of millions of members?
“Continue watching”, resume points and the inputs to recommendations all come from viewing history. At 625k progress writes a second at peak and roughly nine writes for every read, it is one of the busiest stores in the system.
Bad: a relational table of viewing events. One row per event, indexed by profile. Familiar and easy to query, but 625k inserts a second need heavy sharding, and active-active writes in several regions make a single-leader database awkward.
Good: one wide Cassandra row per profile, with a cache. This was Netflix’s first cloud design: the row key is the member ID and each viewing record is a new column, so a write is one cheap column insert and “all history for this member” is one partition read. EVCache in front serves most reads. It ran well for years — and then heavy viewers’ rows grew very wide, reads had to merge many on-disk files, and compaction and repair slowed for everyone.
Great: split recent from old, compress, and chunk. Netflix’s redesign keeps two tables per member.
- LiveVH holds a few recent, frequently updated records, uncompressed, with aggressive compaction so it stays small and fast.
- CompressedVH holds everything older as one compressed value in a single column, rarely updated. When a member’s live records pass a threshold, a background job rolls them into a new versioned compressed copy, reads it back to verify, and only then deletes the old version — no locks, last writer wins on the rare collision.
- Chunking caps the worst case. If a member’s compressed history exceeds a size limit, it is split into chunks on different nodes, keyed by member, version and chunk number, and read in parallel after a small metadata read. Common case: one read and one write. Rare case: two of each.
Netflix reported about 6× less data, 13× less time spent on Cassandra maintenance, 5× lower average read latency and 1.5× lower average write latency. The cost is complexity: a roll-up job, versioning, and two read paths. That is the price of a store whose biggest users grow forever. More on the database in Cassandra and DynamoDB.
How do we launch a new season everywhere at the same moment?
A new season of a hit show is the stress test. Suppose 10M members press play in its first 10 minutes: 10M ÷ 600 s ≈ 16.7k extra play starts a second, on top of a normal evening’s 14k peak, and 10M × 4 Mbps = 40 Tbps of extra video. The trick is that these two loads land on very different systems.
Bad: publish on the day. Encode late, release, and let caches fill on demand. Every appliance misses at once, interconnects saturate, and the first night’s reviews are about buffering.
Good: pre-fill and gate. Encode days ahead, fill appliances through normal nightly windows (or a fast-track fill if the master arrived late), and make the title live only when enough clusters hold enough copies, the rights window is open, and the launch time has passed. The 40 Tbps then comes from appliances that already hold the episodes — and with the 40/60 split, the hottest files sit on every box in a cluster.
Great: also prepare the control plane. The bytes are solved; the decisions are not. Scale the playback, steering, licence and homepage services up before the launch time instead of waiting for autoscaling to notice. Warm the metadata, artwork and rights caches with the new episodes so the first requests do not stampede the databases. Set gateway priorities so that if anything must be shed, play starts are protected and the homepage falls back to non-personalised rows. And add jitter to devices that refresh their homepage on a schedule, so millions of TVs do not ask in the same second.
Launch is a flag, not a deploy
If launching a title requires copying files, invalidating caches or deploying code at the launch moment, you have built a launch-night outage. Everything that moves bytes should finish days earlier; the launch itself should only flip a rights-and-time gate that every service already knows how to read.
The final design
Put it together. The cloud holds every decision and every piece of member data; the edge holds the bytes; encoding and fill connect them, days before anyone presses play.
- Browse. The TV calls the API gateway; the homepage service assembles precomputed rows, filters them to titles licensed in the member’s country, and picks one image per title. Images and title metadata are cached aggressively.
- Press play. The playback API checks the plan and rights window, chooses files for the device — codec, device-capped ladder, audio language, subtitles — and gets a DRM licence URL. Steering, using the appliance state OC control maintains, returns a ranked list of at least three appliances holding those files.
- Stream. The player fetches byte ranges from an appliance inside its ISP, choosing a rung per segment from the title’s own ladder, and fails over to the next URL on errors. The bytes never involve the cloud; only small progress pings and licence renewals go there.
- Remember. Progress pings flow through the gateway to the viewing-history service: recent records into the live table, older ones rolled into the compressed archive, a cache in front for “continue watching”.
- Publish. Days before launch, a licensed master is checked, encoded per title and per shot in three codec families, and stored in S3.
- Fill. Each night, in every region’s fill window, appliances pull the files their manifests list — from peers first, then other tiers, S3 last — and report what they hold. When copies, rights and time line up, the title goes live.
Check it against the requirements. Start-up is fast because the first bytes come from inside the ISP and the ladder starts at a rung tuned for this title. Availability holds through a region failure because the control plane is active-active and in-flight streams never depend on it. 150 Tbps at peak is spread over thousands of appliances, each serving tens of gigabits. Licensing fails closed at play start. Quality per bit is bought once per title with offline compute and repaid on every hour streamed. Resume positions and homepage rows are eventually consistent by design.
What interviewers look for by level
| Area | Mid-level | Senior | Staff |
|---|---|---|---|
| Framing | Video streaming with a CDN | Separates control plane from data plane; notes the licensed, known catalogue | Explains why a known catalogue changes caching, encoding and launch, and what would break with user uploads |
| Delivery | Uses a CDN | Caches in ISPs and exchanges, tiered fill, hit offload | Fill windows, popularity feeds, cluster storage split, ISP relationships and forecast failure |
| Steering | Nearest server | Picks servers that have the file and are healthy | Route-specific ranking, MEDs, ranked lists, client failover, stale-report trade-offs |
| Encoding | Several resolutions | Per-title ladders and the convex hull | Per-shot optimisation, perceptual metrics, codec cost curves, off-peak compute, file explosion |
| Resilience | Multiple instances | Multi-region with replication | Active-active rules, cache invalidation, load shedding, chaos exercises, capacity reserve |
| Data | A database for history | Cassandra keyed by profile with a cache | Wide-row failure mode, live and compressed tables, chunking, read and write bounds |
| Launch | “Autoscaling handles it” | Pre-filled content, liveness gate | Pre-scaling, warm caches, shedding priorities, jittered clients, launch as a flag |
Follow-up questions
What if Netflix streams a live boxing match?
Pre-positioning is impossible: the content exists only seconds before it is watched. A live pipeline encodes in real time into short segments, pushes them through a hierarchy of appliances so each segment crosses each link once, and relies heavily on request coalescing, because every viewer wants the same few seconds at the same moment. Netflix’s Q4 2024 letter called its Paul–Tyson fight the most-streamed sporting event ever, so the same edge fleet serves both modes; what changes is the fill model (continuous push instead of nightly pull) and the latency budget.
What if a title’s licence ends in one country at midnight?
The rights window is checked at every play start, so new plays stop exactly at the boundary. For sessions already running, give the DRM licence its own short expiry and have the player renew it periodically, so playback ends within one renewal interval. The files can stay on appliances — they are encrypted and useless without a licence — and leave in the next delete manifest. The homepage must drop the title too, which is why the catalogue cache is keyed by country and refreshed ahead of known window changes.
What if members want to download episodes for a flight or a metro ride?
Downloads need different encodes: no adaptation is possible offline, so each download is a single stream optimised for a fixed size. Netflix’s per-chunk optimisation work began with exactly these mobile download encodes. Issue an offline DRM licence with an expiry and a limit on devices or downloads per title, renewed when the phone is next online.
What if an ISP has no embedded appliances?
Its members are steered to appliances at the nearest exchange where the ISP peers with Netflix. Steering’s MED offsets already prefer embedded over private interconnect over public exchange, so the same ranking works; the ISP simply sees more traffic on its peering links. If that traffic grows large enough, embedding appliances becomes worthwhile for both sides — which is the whole commercial logic of the programme.
What if the catalogue were user-generated instead?
Then pre-positioning fails, because 20 million new videos a day cannot be forecast, and per-shot encoding of every upload costs far more than most videos will ever repay. The design swings towards resumable uploads, a fast transcoding pipeline, pull-through caches with admission control for an enormous long tail, and codecs earned by popularity. That is design YouTube.
How to say it in the interview
“Netflix is a licensed catalogue — thousands of titles — streamed to about 300 million memberships, with something like 35 to 40 million concurrent streams and around 150 terabits a second at the evening peak. The key fact is that the content is known in advance, so I design for pre-positioning and offline quality, not caching on demand.
I split it into a control plane and a data plane. The control plane runs in the cloud, active-active across regions: homepage, playback API, rights, steering, viewing history. Services are stateless, each region uses only local resources, nothing calls across regions on the request path, and data replicates asynchronously in Cassandra. We rehearse losing a whole region.
The data plane is our own cache appliances inside ISPs and at internet exchanges. Every night, in a twelve-hour fill window when viewing is lowest, each appliance pulls the files its region is predicted to watch, from a peer first and the origin last. A whole new season is a few hundred gigabytes, a small slice of one night’s fill.
When a member presses play, the playback API checks the rights window — failing closed — picks the files for that device, and steering returns a ranked list of appliances that hold them, ranked by the most specific route, AS path, MED and distance. The player streams byte ranges from the first and fails over to the next on its own, so a cloud outage never stops a stream already playing.
Because each title is watched for millions of hours, encoding is worth a lot of compute: per-title ladders on the convex hull, then per-shot optimisation with a perceptual metric, which saves roughly a fifth of the bits or more. Viewing history is a write-heavy Cassandra store with live and compressed tables. And a launch is just a gate that opens when copies, rights and time line up.”
Concepts used in this design
- Content delivery network (CDN) — what Open Connect is, and how it differs from a pull-through CDN.
- Push vs pull architecture — nightly fill pushes content ahead of demand.
- Control plane vs data plane — decisions in the cloud, bytes at the edge.
- Multi-region and active-active — stateless regions with asynchronous replication.
- Disaster recovery — region evacuation as a rehearsed, routine operation.
- Availability and the nines — what a 99.99% goal allows.
- Fault tolerance — client failover across ranked appliances.
- Cassandra and DynamoDB — the write-heavy viewing-history store.
- Distributed caching — EVCache and cross-region invalidation.
- Strong vs eventual consistency — strict rights checks, relaxed resume points.
- Backpressure and load shedding — protecting the control plane on launch night.
- API gateway — Zuul’s routing, failover mode and traffic caps.
- Blob storage (S3) — the origin for every encode.
- Cost-aware design — spending compute once to save bandwidth forever.
References and further reading
- Netflix Open Connect Overview (PDF)openconnect.netflix.comEmbedded and IXP appliances, what OCAs report to the AWS control plane, URL-based steering by file availability, health and proximity, and the five-step playback flow.
- Netflix Open Connect: Embedded appliance deployment guide (PDF)openconnect.netflix.com12-hour fill window from 2 AM local time, up to 1.2 Gbps and 6.5 TB per storage appliance, appliance selection criteria and MED offsets, ranked lists of 3 or more OCAs, and the 40/60 clustering split.
- Netflix and Fill (Netflix TechBlog, 2016)netflixtechblog.comProactive caching in fill windows, manifest and fill clusters, popularity feeds, peer, tier and cache fill, fill masters, and title liveness.
- Serving 100 Gbps from an Open Connect Appliance (Netflix TechBlog, 2017)netflixtechblog.comA single FreeBSD appliance serving about 90 Gbps of TLS traffic from NVMe, with 10–20% of the most popular titles served from memory.
- Per-Title Encode Optimization (Netflix TechBlog, 2015)netflixtechblog.comThe fixed ladder topping out at 5,800 kbps, the convex hull of trial encodes, rungs one JND apart, and examples such as 1080p at 1,540 kbps and a 20% saving at the top rung.
- Dynamic optimizer — a perceptual video encoding optimization framework (Netflix TechBlog, 2018)netflixtechblog.comShot-based encoding with keyframes at shot changes, VMAF, chunked encoding, 5–10× encoder complexity per codec generation, and 17–50% bitrate savings.
- Active-Active for Multi-Regional Resiliency (Netflix TechBlog, 2013)netflixtechblog.comA 99.99% availability goal, stateless services, no cross-region calls on the user path, Cassandra replication, EVCache invalidation, geo-DNS, Zuul load shedding and Chaos Kong.
- Scaling Time Series Data Storage — Part I (Netflix TechBlog, 2018)netflixtechblog.comViewing history in Cassandra: 9:1 writes to reads, live and compressed tables, chunking, EVCache, and the resulting 6× smaller data and 5× faster reads.
- Artwork Personalization at Netflix (Netflix TechBlog, 2017)netflixtechblog.comChoosing one image per title per member, and a peak of over 20 million image-selection requests per second.
- Netflix Q4 2024 shareholder letter (PDF)s22.q4cdn.com301.63 million paid memberships at the end of 2024, and the end of quarterly membership reporting from 2025.