- Live IPL is one stream watched by everyone at the same second. Video bytes are a CDN problem; the hardest backend problems are synchronised surges driven by match events and ad breaks.
- Pipeline: stadium production → redundant contribution feeds → cloud live transcoder (ABR ladder, many commentary languages) → packager (HLS/DASH, CMAF) → origin → shield → multi-CDN → player.
- Low-latency HLS / chunked CMAF cuts glass-to-glass delay from ~30 s to a few seconds, at the cost of request volume and tighter buffers.
- Pre-scale backends and reserve CDN capacity from a forecast; reactive autoscaling is too slow for a jump of millions in a minute.
- SSAI with per-cohort manifests and pre-computed ad decisions survives every over break; a degradation ladder keeps playback alive when everything else is on fire.
Hotstar (now JioHotstar) has publicly reported concurrency records for cricket that were world records for live streaming at the time: around 25 million concurrent viewers during the 2019 World Cup semi-final, and far higher figures reported in later tournaments. Its engineers have also spoken publicly about the techniques behind it: pre-scaling, load testing at enormous scale, and deliberately switching off features under load. "Design Hotstar for IPL" has therefore become a favourite question at Indian product companies.
The question looks like "design YouTube, but live". It is actually a different problem: everyone watches the same thing at the same time, so caching is easy but load is perfectly synchronised. This post is built around that difference. For on-demand video fundamentals (ABR, transcoding ladders, CDN basics), see design YouTube/Netflix.
Why Live Cricket Is Uniquely Hard
- Event-driven surges: concurrency jumps when a star batter walks in, in the last overs of a close chase, or after a rain delay ends. People who left the app all return within a minute, triggering a login and home-screen surge on top of playback.
- Everyone hits the same ad break: at the end of each over, every viewer needs an ad decision and ad segments at once.
- Latency vs spoilers: if your stream is 40 seconds behind TV, WhatsApp groups and neighbours' cheers spoil the six before your users see it.
- Device and network diversity: low-end Android phones on congested 4G in small towns, smart TVs on fibre, all on one stream.
- No retakes: an on-demand outage delays a show; a live outage loses the moment forever, and it trends on social media in seconds.
Requirements
Functional
- Stream live matches in multiple qualities and multiple commentary languages (English, Hindi, Tamil, Telugu, Kannada, Bengali and more).
- Pause and rewind within a live window (DVR), "go live" button, multiple camera angles optionally.
- Live scorecard, key moments, social features (reactions, polls).
- Ads at over breaks and strategic timeouts; subscription tiers.
- Highlights and replays shortly after key moments.
Non-functional
- Scale: 25M+ concurrent viewers with headroom.
- Playback quality: startup within a few seconds, rebuffering ratio well under 1%.
- Glass-to-glass latency: target under ~10 s, ideally close to TV broadcast delay.
- Availability: playback must survive failures of any single component, region or CDN.
- Cost control: bandwidth is enormous; bitrate choices matter.
Capacity Estimates
Interview assumptions, not platform figures:
- 25M concurrent viewers at peak; ramp from 10M to 25M in about 15 minutes during a tense finish, with bursts of ~1M new viewers per minute.
- Average delivered bitrate ~1.2 Mbps (mostly mobile at 480p to 720p, some TVs at 1080p+). Egress = 25M × 1.2 Mbps = 30 Tbps. No single CDN or data center serves that comfortably; multiple CDNs and ISP-embedded caches are mandatory.
- Segment requests: with 4-second segments, each viewer fetches one video segment (plus audio, if separate) every 4 s → 25M / 4 ≈ 6M+ segment requests/s.
- Manifest requests: live playlists are refreshed roughly once per segment duration → another ~6M requests/s. With LL-HLS partial segments of ~1 s, request rates multiply.
- Backend APIs: heartbeats, entitlement checks, scorecard, ads, analytics. Even at one API call per viewer per 20 s, that is 1.25M requests/s, all of which would melt an origin if not cached or batched.
- Ad breaks: ~25M ad decisions needed within a few seconds every over (roughly every 4 minutes).
The Ingest-to-Player Pipeline
Stage by stage
- Production: the broadcaster's production team creates the director's cut with graphics. Commentary for each language is mixed separately; each language becomes a separate audio rendition or a separate feed.
- Contribution: high-bitrate mezzanine streams leave the stadium over redundant paths (dedicated fibre, internet with SRT for loss recovery, satellite as backup) to two independent cloud ingest points. Losing one path must not drop the stream.
- Live transcoding: each feed is encoded in real time into an ABR ladder. Unlike on-demand, there is no second pass: encoders must keep up with real time, so they run on dedicated capacity (often hardware-accelerated) with 1+1 redundancy. Keyframes are aligned across renditions so players switch quality cleanly.
- Packaging: produce HLS and DASH manifests referencing CMAF segments (one set of media files for both protocols), apply DRM, insert SCTE-35 cue markers at ad break boundaries, and maintain a DVR window (say the last 2 hours) for rewind.
- Origin and shield: origins store the rolling window of segments; an origin shield layer absorbs CDN misses so origins see only a handful of requests per segment.
- Distribution: multiple CDNs plus caches inside ISPs (critical in India, where a few large mobile operators carry most viewers) serve the bytes.
- Playback: the player picks a CDN (via steering), fetches manifests and segments, adapts bitrate, inserts or plays ads, and reports quality-of-experience telemetry.
Redundancy everywhere
Every stage runs at least 1+1: two contribution paths, two ingest regions, two encoder chains per language, two packagers writing to replicated origins. Packagers use synchronised timestamps so that the two chains produce interchangeable segments; a player or CDN can fail over between them without a glitch.
Low-Latency HLS: Getting Close to TV
Where the delay comes from
| Component | Classic HLS (6 s segments) | Low-latency (CMAF chunks / LL-HLS) |
|---|---|---|
| Capture, production, contribution | 1 to 3 s | 1 to 3 s |
| Encoding | 1 to 2 s | ~1 s |
| Segment duration (must finish before publishing) | 6 s | Partial segments of ~0.3 to 1 s published while encoding |
| CDN propagation | ~1 s | ~0.5 s |
| Player buffer (typically ~3 segments) | ~18 s | ~1 to 3 s |
| Total glass-to-glass | ~25 to 30 s | ~4 to 8 s |
How low latency is achieved
- Chunked CMAF: a segment is split into small chunks that are sent over HTTP chunked transfer encoding as soon as each is encoded, so the player can start playing a segment before it is complete.
- LL-HLS (Apple's extension): manifests advertise partial segments, support blocking playlist reload (the server holds the manifest request until the next part exists, avoiding wasteful polling) and preload hints so the player requests the next part before it is published.
- Smaller buffers: lower latency means less buffer to absorb network jitter, so rebuffering risk rises. Players adjust playback speed slightly (e.g. 0.95x to 1.05x) to keep a target latency without stalling.
The trade-off to state in the interview
Low latency multiplies request rates (more, smaller objects), stresses CDNs that must support chunked delivery and blocking requests, and increases rebuffering on poor networks. A sensible design offers low-latency mode on good connections and falls back to a larger buffer for viewers on weak networks, where a smooth stream matters more than 10 seconds of delay. Also keep latency consistent across viewers; a friend's phone 15 seconds ahead is as bad as being behind TV.
Manifests and the Request Math Nobody Mentions
In a live stream, the media playlist changes every few seconds. A simplified live HLS playlist with an ad marker looks like this:
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:18432
#EXT-X-PROGRAM-DATE-TIME:2026-04-12T14:32:08.000Z
#EXTINF:4.000,
seg_18432.m4s
#EXTINF:4.000,
seg_18433.m4s
#EXT-X-CUE-OUT:DURATION=30
#EXTINF:4.000,
ad_slot_18434.m4s
Every player re-fetches this file roughly every target duration. At 25M viewers that is millions of requests per second for a file that is identical for everyone (per rendition and language). The design consequences:
- Cache manifests at the CDN with a TTL of about half the segment duration (e.g. 1 to 2 s). The edge serves millions of requests from one cached copy.
- Request collapsing at edges and shields: when the cached manifest expires, only one request per edge goes upstream; others wait for it.
- Segments are immutable once published; cache them with long TTLs (the DVR window).
- Personalised manifests destroy caching. This matters enormously for ads, next section.
- Program date time tags let players align playback with wall-clock time, which is how you synchronise latency across devices and the scorecard with video.
CDN Strategy and Pre-Scaling
At ~30 Tbps, CDN capacity is a negotiated, scheduled resource, not an elastic one.
- Capacity reservation: forecast peak concurrency per region and ISP for each match, then reserve capacity with each CDN partner days in advance. CDNs themselves pre-position servers and bandwidth for known events.
- Multi-CDN with steering: a control plane assigns each session a CDN based on region, ISP, real-time QoE (startup time, error rate, throughput from player telemetry) and remaining reserved capacity. Players can fail over mid-stream to another CDN's URL for the next segment.
- ISP-embedded caches: caches inside Jio, Airtel and Vi networks serve traffic without crossing peering links, which are often the real bottleneck during a big match.
- Origin shield + collapsing: for a live stream, the working set is tiny (the last few segments per rendition and language), so hit ratios are extremely high, provided misses are collapsed.
- Bitrate as a capacity lever: if a region approaches its capacity limit, cap the maximum rendition there (e.g. 720p instead of 1080p) via the steering or manifest layer. A slightly softer picture for some is better than buffering for everyone.
Scaling Backends Ahead of the Spike
Reactive autoscaling (scale when CPU exceeds 70%) fails for live sports. Instances take minutes to boot, pull images, warm caches, open connection pools and pass health checks, while traffic can jump by millions of users within a minute when something dramatic happens. Hotstar engineers have publicly described scaling on concurrency forecasts rather than CPU for exactly this reason.
The pre-scaling playbook
- Forecast concurrency per match from history: teams playing, stage of tournament, day and time, star players, previous matches' curves.
- Translate concurrency to capacity per service using measured requests-per-viewer and per-instance throughput from load tests.
- Scale before the toss to the level for the next stage of the match, with lead time; keep the ladder one step ahead.
- Do not scale down mid-match. A lull after a wicket can turn into a surge 10 minutes later.
- Use concurrency as the scaling signal (from player heartbeats), not CPU, for any reactive top-up.
- Keep warm pools of pre-initialised instances and make sure cloud capacity (and quotas) is actually available in the region; capacity reservations exist for this.
import math
def scaling_plan(forecast, rps_per_viewer, rps_per_instance, headroom=1.5, lead_min=15):
"""forecast: list of (minute_offset, expected_concurrent_viewers).
Returns (minute_to_start_scaling, target_instances) steps, issued lead_min early
because instances take minutes to boot, warm caches and pass health checks."""
plan = []
for minute, viewers in forecast:
needed = viewers * rps_per_viewer * headroom / rps_per_instance
plan.append((minute - lead_min, math.ceil(needed)))
# never scale down during the match: surges come back at the next wicket
peak_so_far, out = 0, []
for minute, n in plan:
peak_so_far = max(peak_so_far, n)
out.append((minute, peak_so_far))
return out
# Toss at T+0, first ball at T+30, expected death-overs peak at T+180
forecast = [(0, 5_000_000), (30, 12_000_000), (120, 18_000_000),
(180, 25_000_000), (200, 15_000_000)]
for start, n in scaling_plan(forecast, rps_per_viewer=0.05, rps_per_instance=2_000):
print(f"T{start:+d} min -> {n} API instances")
# T-15 min -> 188 API instances
# T+15 min -> 450 API instances
# T+105 min -> 675 API instances
# T+165 min -> 938 API instances
# T+185 min -> 938 API instances
In Kubernetes terms, this becomes scheduled updates to replica counts (or HPA minimums) plus cluster node pre-provisioning; see Docker and Kubernetes for backend engineers. The essential idea is that capacity is a function of the match schedule, decided in advance and validated by load tests at full forecast scale.
Avoid self-inflicted surges
- Login storms: long-lived sessions so returning viewers do not all re-authenticate; entitlement checks cached in signed tokens rather than queried per segment.
- Client retries with jitter and backoff: a 5-second API blip must not turn into 25M synchronised retries.
- Push notifications: "Kohli is on 99" sent to 50M users creates the surge you are trying to survive. Stagger sends over minutes, by cohort.
- Home screen: during a live match, the home page can be a mostly static, CDN-cached response with the match card on top.
Ad Insertion at Every Over Break
CSAI vs SSAI
| Client-side (CSAI) | Server-side (SSAI) | |
|---|---|---|
| How | Player pauses content, calls ad server, plays ad with separate player | Ad segments stitched into the stream manifest; player sees one stream |
| Experience | Visible switching, possible blank/buffering | Seamless, TV-like |
| Ad blockers | Easier to block | Hard to block |
| Scale challenge | 25M clients call the ad server simultaneously | Manifest personalisation breaks CDN caching |
Making SSAI scale: cohorts and pre-computed decisions
- Cohort manifests: instead of a unique manifest per viewer, group viewers into cohorts (e.g. city × language × device × a few targeting segments, maybe a few thousand cohorts). Each cohort gets its own manifest variant, cacheable at the CDN. Targeting is slightly coarser; caching works.
- Pre-computed ad decisions: the SCTE-35 markers tell you a break is coming. Before each break, the ad decision service computes the pod (sequence of ads) for every cohort, respecting campaign pacing, frequency caps and sponsor commitments, so nothing needs deciding at the instant the break starts.
- Pre-transcoded creatives: ads are transcoded into the same ladder, codecs and segment boundaries as the live stream ahead of time and pre-warmed in CDNs; an ad that has to be transcoded on demand will miss the break.
- Tracking beacons: impression and quartile events from 25M viewers per break are batched on the client and sent with jitter to a collector that writes to Kafka; billing aggregates them downstream (the same pattern as ad click aggregation).
- Fallback: if the ad system is late or down, the stream must continue: fill the slot with a default sponsor slate or the broadcaster's own break content. Never stall playback waiting for an ad.
Graceful Degradation: The Panic Button
When load exceeds capacity, the worst outcome is uniform failure. The best outcome is that video keeps playing for everyone while lower-priority features turn off. Define a ladder in advance, wire every level to a feature flag that clients read from a CDN-cached config, and rehearse it.
| Level | Trigger (example) | Actions |
|---|---|---|
| 0 Normal | Everything healthy | All features on |
| 1 Elevated | Backend latency rising | Disable personalised recommendations (serve static rows), increase API cache TTLs, slow telemetry frequency |
| 2 High | Sustained overload | Sample social reactions (show aggregates, not every emoji), disable comments, freeze non-match home content, stop non-critical background calls |
| 3 Critical | CDN or core APIs near limits | Cap max bitrate per region, skip non-essential ad personalisation (use default pods), serve entitlement from cached tokens only |
| 4 Survival | Partial outage | Static match page from CDN, playback-only mode, retry-after hints to clients |
Design principles behind the ladder:
- Classify every API as critical (playback, entitlement), important (scorecard, ads) or optional (recommendations, social, profile edits).
- Clients must tolerate missing features: a failed recommendations call renders an empty row, not an error screen.
- Static fallbacks for every critical screen, pre-generated and cached at the CDN.
- Per-dependency timeouts and circuit breakers so one slow service cannot hold threads needed by playback.
Scorecard, Key Moments and Reactions
Real-time features are where candidates over-engineer. Opening 25M WebSocket connections just to push a scorecard is expensive and fragile.
- Scorecard as a cached JSON: the score service publishes a small JSON per match, cached at the CDN with a 1 to 2 second TTL; clients poll it with jitter. 25M polls every few seconds become mostly edge hits.
- Sync with video, not wall clock: because the stream is delayed, showing "SIX!" from the scorecard before the video shows it is a spoiler. Tag score updates with the program date time of the ball and let the player reveal them when playback reaches that timestamp.
- Reactions and polls: clients send events in batches; a stream processor aggregates counts per few seconds; the aggregate is published like the scorecard. Nobody needs every individual emoji, only the swelling totals. Leaderboard-style features for fantasy contests use the patterns in real-time leaderboard design.
- Key moments and highlights: operators (or ML models) mark timestamps; a clipping service cuts segments from the DVR window into a short on-demand asset within minutes, no re-encoding needed when cuts align to segment boundaries.
Quality-of-Experience Monitoring
Server metrics alone do not tell you if people can watch. Player telemetry is the ground truth.
- QoE metrics: video start time, startup failures, rebuffering ratio, average and distribution of bitrate, playback errors, live latency, by CDN × ISP × region × device × app version.
- Real-time dashboards with second-level granularity during matches; alerts on deviations from the expected concurrency curve (a sudden drop means a failure, not disinterest).
- Automated actions: the CDN steering system shifts traffic away from a CDN-ISP pair whose rebuffering rate spikes, within a minute, without human intervention.
- Synthetic probes from many cities and ISPs playing the stream continuously.
- War room: big matches run with a staffed command centre, pre-agreed runbooks and owners for each degradation lever.
For the general telemetry stack (metrics, logs, traces), see observability explained.
Interview Guide
A 45-minute structure
- Clarify: live only or VOD too? Latency target? Languages? Ads? (3 min)
- Estimate: egress Tbps, segment and manifest request rates, ad decisions per break. (5 min)
- Pipeline: contribution, transcode, package, origin, shield, multi-CDN, with redundancy. (10 min)
- Latency: classic vs low-latency HLS and the trade-offs. (5 min)
- Surges: pre-scaling, CDN reservation, avoiding self-inflicted storms. (8 min)
- Ads: SSAI with cohorts and pre-computed decisions. (6 min)
- Degradation and monitoring. (8 min)
Follow-ups to expect
- "How is this different from Zoom?" Zoom is many-to-many with sub-second latency over WebRTC and SFUs; Hotstar is one-to-many where seconds of latency are acceptable and HTTP caching does the heavy lifting. See Zoom design.
- "What if the primary ingest region fails mid-over?" The second chain is already producing timestamp-aligned segments; the origin or CDN switches source, players continue with at most a brief glitch.
- "How do you test for 25M users?" Load testing with distributed generators that emulate real player behaviour (manifest polling, ABR switching, API calls), run against production-like infra before the season, plus chaos drills.
- "How would you cut bandwidth cost?" Per-device ladders, better codecs (HEVC/AV1) where devices support them, regional bitrate caps during peaks, and ISP-embedded caches that reduce transit cost.
Common mistakes
- Treating it as on-demand YouTube and spending time on upload pipelines.
- Relying on reactive autoscaling for event-driven surges.
- Per-user manifests for ads without addressing CDN cacheability.
- Pushing every scorecard update and emoji over WebSockets to 25M clients.
- No redundancy in contribution and encoding: a single encoder is a single point of failure for the entire audience.
- No plan for degradation; "the system is scaled" is not a plan.
To round out your video preparation, compare this with Instagram Reels, where the challenge flips to many different videos and per-swipe startup.
Frequently Asked Questions
Master Large-Scale System Design
Prepflix's system design course covers Hotstar, YouTube and Zoom-scale streaming designs with capacity drills and mock interviews run by engineers from top product companies.
Explore the Prepflix Course →