Live Sports Streaming at Scale
The 2018 FIFA final drew a combined 1.12 billion live viewers. This chapter follows one frame of that stream from the camera to the phone — encoding, edge caching, real-time validation, Kafka's speed tricks, storage math, and how you keep the whole thing standing on game day when traffic climbs by millions of hits per minute.
Assumes you know
After this you should be able to
- Explain the core idea in Live Sports Streaming at Scale
- Apply Live Sports Streaming at Scale in a representative design
The 2018 FIFA final drew a combined 1.12 billion live viewers. This chapter follows one frame of that stream from the camera to the phone — encoding, edge caching, real-time validation, Kafka's speed tricks, storage math, and how you keep the whole thing standing on game day when traffic climbs by millions of hits per minute.
This is the first full system-design chapter — a sports service like ESPN, Cricbuzz, and Hotstar. It's built as a pipeline you can trace end to end: the live stream's journey from stadium to screen, then the supporting systems (validation, buffering, storage) and finally the operational playbook for surviving peak traffic. Each concept is a reusable building block you'll see again in later chapters. Anything marked [+] is context I added; everything else is rewritten from the ten teaching lessons.
One website, six real-world systems inside it#
The service covers sports internationally through articles, real-time commentary, live streaming, and more. For scale context: Cricbuzz sees ~220M monthly visitors; Hotstar handles ~420M users with 10–25M concurrent during peak cricket season.
The design trick is that the homepage isn't one application — each section is powered by a separate backend service, and each is itself a full-blown distributed system. Build them independently, then club them together via APIs and message queues:
This is how YouTube and Netflix are actually built: microservices/modules with distinct responsibilities, communicating over APIs and message queues. Learn to design one such service and you understand the anatomy of many others.
From camera to codec: encode, transcode, segment#
Picture the FIFA final, England vs. France, with two traffic hotspots. Raw high-resolution video is enormous — it exhausts bandwidth, burns compute and storage, and pushes latency up. The whole first-mile pipeline exists to shrink that latency.
- Encoding — compress the file size without hurting quality, using codecs like H.264 (AVC), H.265 (HEVC), H.266 (VVC), AV1, VP9, MPEG-2. [+] Newer codecs trade more CPU for smaller files. (YouTube engineering famously moved to VP9.)
- Transcoding — re-encode into multiple formats, resolutions, and aspect ratios so the stream plays on any device under any network. The stream should never stop; quality may drop, but playback continues.
- Segmentation — split the stream into 1–6 second chunks so the client plays the first segment immediately instead of waiting for a whole file.
- Decoding — the client's player uncompresses each segment and plays it.
Adaptive bitrate streaming
Bitrate is data-per-unit-time (Mbps); higher bitrate = higher quality. Transcoding produces the same stream at several bitrates, and the client's adaptive bitrate algorithm switches between them at segment boundaries based on current bandwidth. Weak network → next segment comes in at a lower bitrate; the stream keeps rolling.
Protocols & knowing your audience
Multiple protocols cover different legs: RTMP for the first mile (camera → encoder) and often the last mile; HLS or MPEG-DASH for delivery to devices; SRT is another transport option. When designing, know the audience: typical bandwidth, rural vs. urban infrastructure, mobile vs. Smart TV, and content type (a 2D cartoon compresses far more cheaply than a live match).
The last mile: edge caches and the thundering herd#
The backend does far more than encode — it stitches ads, embeds live scores/graphics, adds the broadcaster logo, restreams to YouTube/Facebook, runs real-time monitoring, and stores the stream for later. Once packaged, the stream is pushed from the origin data center to edge locations worldwide, nearest to each viewer. Edge nodes act as caches: fetch from origin once, serve many nearby viewers, minimizing latency and origin load.
Suppose a segment isn't in the edge cache yet. Half a billion concurrent viewers hit the cache, miss, and the requests stampede toward the origin — overwhelming it into lag, dropouts, and eventual collapse.
The fix is request coalescing: requests are never let through to origin. The edge node queues all requests for a missing segment, fetches from origin once, populates the cache, and fans the single response out to every waiting viewer.
Is it rendering smoothly? Real-time validation with ELK#
Once the stream is live, you need a system that continuously confirms it renders well on viewers' devices, watches for errors, and alerts on-call staff instantly. Viewer devices emit video playback logs into an ELK stack:
| Component | Role |
|---|---|
| Elasticsearch | Distributed, RESTful, JSON-based full-text search & analytics engine — stores and indexes the logs. |
| Logstash | Aggregates logs from many sources, transforms them to a common format, and ships them onward. |
| Kibana | Web UI to visualize, query, and analyze the data via graphs and charts. |
Log management is just one ELK use case — it's also used for app metrics, performance, endpoint security, uptime, and application search.
Archiving old data — and why tape still matters [+]
Keep recent data (≤ ~1 year) in Elasticsearch; archive older data to cloud archival storage, which cuts cost dramatically and frees space, while still serving archived data via API in milliseconds (no tape-style retrieval delay). But cloud storage isn't a 100% replacement for tape — software bugs can inadvertently delete data. Two real Google incidents make the case:
The takeaway: combine cloud archival and tape backups for critical data. (Source: Google Research, Designing and Operating Highly Available Software Systems at Scale.)
Why Kafka sits between the devices and Logstash#
Why not push logs straight to Logstash? During a live event the log influx is massive, and Logstash's filtering/transforming plus Elasticsearch's indexing all take time. Under that velocity, the pipeline bottlenecks. Kafka is placed in front of Logstash as a buffer — Logstash consumes from Kafka topics at its own pace, exactly like the edge cache absorbing the thundering herd.
Kafka fundamentals
- Producers, consumers, topics. Producers write events to a topic; consumers subscribe and
pull. Many producers/consumers can share a topic (e.g. topic
android logsfor all Android-device logs). Producers and consumers are decoupled — neither waits on the other. - Push or pull. Producers push to topics; consumers pull. Pull lets each consumer read at its own processing speed without being overwhelmed.
- Broker & cluster. A broker (= node = server) runs one Kafka process; a cluster has multiple brokers (min 3).
- Partitioning. A topic splits into partitions across brokers for parallelism. Events append to a partition by key, and Kafka guarantees order within a partition.
- Replication. Topics are replicated across availability zones/regions; a typical production replication factor is 3.
- Extras. Kafka Connect imports/exports from other systems; Kafka Streams processes streams. Deployable on bare metal, VMs, containers, on-prem, across regions.
Because Kafka is highly available, fault-tolerant, and O(1) with respect to data size, it can durably store events for as long as you want — like a distributed database that also streams.
What makes Kafka so fast (it leans on the disk)#
Counterintuitively, Kafka relies on disk + the filesystem rather than in-memory caching. Disks are slow or fast depending on how you use them — a well-designed disk structure can be as performant as you need. Four techniques do the heavy lifting:
| Technique | Why it's fast |
|---|---|
| Sequential I/O | Kafka uses a segmented, append-only log, restricting reads/writes to sequential access. In a benchmark on a 4 GB on-disk array: sequential disk read = 53.2M values/sec vs. random memory access = 36.7M/sec — sequential disk beats random memory. (Source: The Pathologies of Big Data.) |
| Page cache in RAM | The OS keeps disk pages cached in free RAM. Kafka rides this, effectively doubling available cache; and because it uses a compact byte structure (not JVM objects), it dodges garbage-collection penalties. The page cache stays warm across restarts, unlike an in-memory cache that must rebuild. |
| Batch compression | Compresses bundled messages together (not individually), stores them compressed, and ships a whole batch in one request — uncompressed by the consumer. Batches cap by count or a short wait (10–20 ms), trading a little latency for throughput. Supports GZIP, Snappy, LZ4, ZStandard. |
| Zero-copy transfer | Normally data is copied between user-space and kernel-space buffers. Zero-copy bypasses the kernel-space buffer, moving data straight to hardware — a big speed jump Kafka exploits. |
Because topics are partitioned across brokers, Kafka processes data in parallel — the same throughput lever every distributed system pulls.
Saving the stream for replays (object storage + a DB map)#
For replays, highlights, and key moments, persist the live stream to cloud object storage (how YouTube stores video). The critical design point:
Store the video files on disk via object storage; keep a database table that maps video ids → disk locations plus metadata (sport, date, venue). Objects live in globally-named buckets tied to a region; each object holds video data + metadata.
A file's disk path might be
https://livestream.ourwebsite.com/football/engvsfrance_fifa_20220417.mp4. The host part goes into a
config/property file; the path part (/football/engvsfrance_fifa_20220417.mp4) goes into the MySQL
LiveStream table. You can even factor /football into config and assemble the full path
at fetch time — it depends on the use case.
Storage estimation for one game [+]
A 2-hour match at ultra-HD 8K compresses to ~28 GB. Transcoding into lower ladders adds up:
| Resolution | Approx. file size |
|---|---|
| 8K (7680×4320) | 28 GB |
| 4K (3840×2160) | 14 GB |
| 1080p (1920×1080) | 6 GB |
| 720p (1280×720) | 3 GB |
| 360p (480×360) | 2 GB |
| Total / game | ≈ 53 GB |
Rough numbers — actual size varies with bitrate, bit depth, codec, resolution, and frame rate (size references from Netflix). Cloud storage cost depends on data size, instance types, number of operations, bandwidth, cross-region replication, daily backups, and storage class.
Game-day readiness: observability and injecting chaos#
These practices apply to any distributed service, not just streaming. Step one is deep visibility with no blind spots — every module monitored.
- Observability vs. monitoring. Observability is a property of the system (how observable is it?); monitoring is what you do to achieve it.
- Telemetry = metrics + logs + traces. Metrics are numeric state over time (CPU %, uptime); logs go with the code and are checked first when something breaks; traces follow a request across services.
- Distributed tracing. Each request gets a unique trace ID; every operation (a span) carries that ID plus its own id and context — invaluable in multi-service architectures. Popular tools: Zipkin, Jaeger.
Synthetic traffic, then chaos
Before the real load hits, send synthetic traffic from different cloud regions to test availability, performance, and transaction flows (login, subscription) under artificial peak load — surfacing bottlenecks and regional errors early.
Netflix built Chaos Monkey to randomly kill production instances, then watch whether services survived — driving them to build automatic recovery. The wider "Simian Army": Latency Monkey (injects delays), Conformity Monkey (kills non-conforming instances), Doctor Monkey (removes unhealthy ones), Janitor Monkey (now Swabbie — reaps unused resources), and Chaos Kong (kills an entire AWS region). Many now live inside Spinnaker, Netflix's open-source continuous-delivery platform.
Don't trust autoscaling: ladders, degradation, throttling#
Capacity planning & ladder-based scaling
Estimate average and peak game-day traffic from past game data and load tests. Then here's the key warning: don't rely on autoscaling for sudden spikes. New instances need provisioning, warm-up, and cluster registration — minutes you don't have when traffic climbs by millions of hits per minute. Worse, the cloud may not have the instance types you need in that region right then.
Instead use ladder-based scaling with pre-provisioned buffers:
50K–100K hits/min → x instances running (even if underutilized) 100K–150K hits/min → x + 20 instances 150K–300K hits/min → x + 40 instances ...as traffic climbs the ladder, pre-defined instances spring to life
A ready buffer means no warm-up wait. Even so, there's a financial ceiling on instances/clusters — so two more levers matter.
Graceful degradation
Services have priorities. During the event, streaming, sign-in, and subscription payment are high-priority; movie recommendations, feedback, and search are not. Under exceptional load, reallocate resources from non-priority services to priority ones — the low-priority services still respond, just more slowly. That's graceful degradation.
Throttling & rate limiting
Throttling controls the rate of events to keep servers from being overwhelmed. Applied on the backend to an API it's called rate limiting — it caps requests per time window, adds a security layer against bots/scraping/DDoS, and underpins SaaS request-based pricing. Algorithms: token bucket, leaky bucket, fixed window counter, sliding window, sliding window counter.
Rate limiting alone isn't enough — clients keep sending, wasting bandwidth and forcing the backend to keep
running its limiting logic. Pair it with client-side rate-throttling: when the client
starts getting HTTP 429 (too many requests), it slows down. Two guardrails: keep the
average retry rate minimum (few 429s, saving bandwidth) and the maximum
sleep time minimum (don't make users wait longer than necessary) — while never leaving the API
under-consumed.
Recap#
Every hard problem in live streaming is the same problem wearing a different mask: a wave of demand arriving faster than a downstream system can absorb it. The camera outruns the network (transcode + segment). Viewers outrun the origin (edge + coalescing). Devices outrun Logstash (Kafka buffer). Game-day traffic outruns provisioning (ladders + degradation + throttling). The whole chapter is one technique applied over and over — put a buffer between the surge and the thing that would break, and let the slow side pull at its own pace.
Further reading#
The links below all resolve live, grouped by topic. The author's own course cross-links and one source that failed to load (a scaleyourapp.com blog on 100M-user scale) are omitted.
Streaming & codecs
Observability & the ELK stack
Kafka performance
Reliability, chaos & storage