CDN Live Streaming Architecture: Ingest, Transcode, Package, and Deliver at Scale

Last year I watched a major sports broadcaster lose 40 seconds of a championship final because their primary ingest encoder hiccupped and the backup took too long to cut over. Viewers didn’t see a spinner — they saw a frozen frame while Twitter exploded. That’s the reality of live streaming architecture: there’s no undo button, no cache to hide behind. You’ve got about two seconds to ingest, transcode, package, and push bits to the edge before your audience notices.

The Ingest Pipeline: Where Live Streams Live or Die

Every live workflow starts with ingest, and it’s the most unforgiving part of the whole chain. Your encoder on-site — whether it’s a hardware box at a stadium or an OBS instance in someone’s bedroom — has to push a single, pristine contribution feed to your ingest point of presence (PoP). For years RTMP was the default because it was simple and every encoder supported it, but it wasn't built for the public internet. I've spent more nights than I'd like to admit debugging RTMP disconnects over congested venue Wi-Fi. These days we don't put a high-value stream on RTMP unless we have no other choice.

Modern ingest pipelines have moved to SRT and RIST for their resilience. They handle packet loss, jitter, and latency much more gracefully thanks to ARQ (Automatic Repeat Request) and better congestion control. In our current Low-Latency HLS Configuration for CDN: Sub-3-Second Live Streaming deployments, we run SRT in listener mode at the ingest origin with redundant inputs. The encoder pushes two SRT streams over diverse ISP paths to two separate ingest endpoints, and we use a stream coordinator to pick the healthiest feed frame-by-frame. It adds complexity on the front end, but it’s saved us during at least three fiber cuts I can recall. Don't forget to lock down your ingest with authentication and TLS — an open ingest endpoint will be found and abused within hours.

# SRT ingest listener with redundant inputs and automatic failover
# Primary and backup encoders push to these ports

srt_live:
  listen: srt://0.0.0.0:9000?mode=listener&latency=2000&passphrase=secure_key
  backup: srt://0.0.0.0:9001?mode=listener&latency=2000&passphrase=secure_key

  # Failover logic: switch if primary packet loss > 5% for 2s
  failover:
    strategy: seamless_switch
    healthcheck_interval: 500ms
    switch_threshold: 5%

  output: rtmp://transcode-origin.internal/live/stream_key

# Nginx-style health endpoint for ingest monitoring
health_check /ingest_status {
  check_srt_stat: packet_loss, rtt, bitrate
  alert_if: rtt > 300ms or bitrate < 3000k
}

Building the Transcoding Ladder Without Burning Your Budget

Once that clean feed hits your ingest server, you need to make it watchable for everyone from a viewer on 4K fiber to someone on a shaky 3G connection in a taxi. That’s where transcoding comes in. You’re taking that single 8-12 Mbps 1080p60 contribution stream and building an adaptive bitrate (ABR) ladder — typically 5 to 7 renditions. I learned the hard way that more renditions aren't always better. On one early project we generated 10 renditions "just in case" and our transcode costs tripled while our rebuffering rate barely moved. The player was just confused.

A practical ladder for 1080p live today might be: 1080p at 4.5 Mbps, 720p at 2.8 Mbps, 480p at 1.2 Mbps, 360p at 700 Kbps, and 160p at 300 Kbps for audio-only fallback. For codecs, H.264 is still the baseline for compatibility, but HEVC and AV1 are creeping into live where hardware decode is guaranteed. The real decision is where you transcode. Software encoding with x264 gives you the best quality per bit, but you’ll need serious CPU. We’ve shifted most live transcoding to GPU-accelerated pipelines (NVIDIA NVENC or Intel Quick Sync) for scale. You lose maybe 5-8% compression efficiency, but you gain 4x density per server. According to the HTTP Working Group Specifications and recent web.dev Articles on media delivery, consistent GOP structure and aligned keyframes across all renditions are non-negotiable for smooth switching.

# Live ABR transcoding ladder with fragmented MP4 output for CMAF
ffmpeg -i srt://ingest-origin:9000 \
  -c:v h264_nvenc -preset p4 -rc cbr_hq \
  -filter_complex "[0:v]split=5[v1][v2][v3][v4][v5];
   [v1]scale=w=1920:h=1080[v1080];
   [v2]scale=w=1280:h=720[v720];
   [v3]scale=w=854:h=480[v480];
   [v4]scale=w=640:h=360[v360];
   [v5]scale=w=256:h=144[v144]" \
  -map "[v1080]" -b:v:0 4500k -g 60 -keyint_min 60 -map a:0 -b:a:0 128k \
  -map "[v720]"  -b:v:1 2800k -g 60 -keyint_min 60 -map a:0 -b:a:1 128k \
  -map "[v480]"  -b:v:2 1200k -g 60 -keyint_min 60 -map a:0 -b:a:2 96k \
  -map "[v360]"  -b:v:3 700k  -g 60 -keyint_min 60 -map a:0 -b:a:3 64k \
  -map "[v144]"  -b:v:4 300k  -g 60 -keyint_min 60 -map a:0 -b:a:4 64k \
  -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 v:3,a:3 v:4,a:4" \
  -f hls -hls_time 2 -hls_flags independent_segments \
  -master_pl_name master.m3u8 /origin/live/stream_%v.m3u8

Packaging Live Segments for HLS, DASH, and Everything In Between

Transcoded renditions are useless until they’re packaged into something a player can request over HTTP. This is the step that bridges traditional video engineering and CDN delivery. The packager takes your continuous encoded GOPs and slices them into short segments — typically 2 seconds for low-latency, 6 seconds for standard latency — and generates the manifests (.m3u8 for HLS, .mpd for DASH). If your transcoder didn’t produce aligned keyframes and identical segment boundaries, your packager will either fail or, worse, produce manifests that cause players to stall on rendition switches.

We standardized on CMAF a couple of years ago and haven't looked back. Instead of packaging twice — once for HLS and once for DASH — we package once to fragmented MP4 (fMP4) chunks and serve both protocols from the same segments. Our origin packager writes CMAF chunks to shared storage and the CDN edge just rewrites the manifest format on the fly. This is exactly what CMAF Packaging for CDN Delivery: Unified Streaming Format for HLS and DASH enables, and it cuts origin storage and packaging compute nearly in half. For protocol selection, we still guide most customers through DASH vs HLS for CDN Streaming: Protocol Comparison and Selection Guide because device support still matters — Safari on iOS still wants HLS, while many smart TVs prefer DASH with broader DRM support.

Packaging Parameter Standard Latency (6s) Low-Latency Mode (2s + Parts) Impact on CDN
Segment / Part Duration 6 seconds 2s segment / 200-500ms parts 3x more origin requests, needs HTTP/2 push or chunked transfer
GOP / Keyframe Interval 2 seconds (60 frames @30fps) 1 second (30 frames @30fps) Shorter GOP increases bitrate by ~8-12% but improves switching
End-to-End Latency 12 - 30 seconds 1.8 - 3.5 seconds Requires edge cache TTL < 1s, origin shield critical
Origin-to-Edge Cache Hit Ratio 98.5%+ 85% - 92% Lower hit ratio needs larger origin egress and tiered caching
Manifest Update Frequency Every 6 seconds Every 200-500ms (LL-HLS / LL-DASH) Increases manifest traffic 10x, use delta updates

Delivering at Scale: Origin Shield, Tiered Cache, and Edge Config That Survives the Spike

This is where most live architectures fall apart. You've got perfect ingest and a clean ABR ladder, but 100,000 viewers all hitting your origin for the same 4-second segment at the same time will melt it in seconds. You don't scale live by adding more origin — you scale it by making sure almost nobody ever hits origin.

For live, we run a strict tiered cache hierarchy. Viewers hit edge PoPs (150ms TTL for segments, 1-2s for manifests), edges collapse to a regional shield, and only the shield talks to origin. With proper request collapsing and a 95%+ edge hit ratio, your origin sees maybe 5-10 req/s for a million-viewer event instead of 250,000. Don't use the same cache rules you use for VOD. For live HLS/DASH, your segments are immutable — cache them for 30-60s with stale-while-revalidate — but your manifests (.m3u8 / .mpd) must be tiny TTLs. We typically set `max-age=2, stale-while-revalidate=2` for live manifests and `max-age=1` for low-latency. And never, ever cache 404s for more than 1s on live manifests, or you'll black-hole viewers during rendition switches.

The other killer is cache key fragmentation. One misconfigured query string and your hit ratio drops from 98% to 40%. Normalize your keys, strip useless trackers at the edge, and include only what matters: path + rendition. Here's the kind of edge logic we deploy on Varnish/Fastly/NGINX to keep live delivery tight:

# Edge logic for live HLS/DASH - VCL / NGINX pseudo-config
# Do NOT cache manifest 404s, collapse segment requests

sub vcl_recv {
    # Normalize cache key - strip marketing params for live segments
    set req.url = regsuball(req.url, "(\?|&)(utm_[^&]*|fbclid[^&]*)", "");
    
    # Manifests: short TTL, pass through revalidation
    if (req.url ~ "\.(m3u8|mpd)$") {
        set req.ttl = 2s;
        set req.grace = 2s;
        return(hash);
    }
    # Segments (.ts, .m4s, .cmfa/.cmfv): immutable, long TTL
    if (req.url ~ "\.(ts|m4s|cmf[av])$") {
        set req.ttl = 45s;
        set req.grace = 30s;
        # Enable request collapsing - critical for live
        set req.hash_always_miss = false;
        return(hash);
    }
}

sub vcl_hit {
    # Serve stale manifest while fetching fresh in background
    if (obj.ttl == 0s && obj.grace > 0s) {
        return(deliver);
    }
}

Advanced Patterns and Troubleshooting: When Live Goes Sideways

Once your basic delivery is solid, you'll get asked for the three features that complicate everything: ultra-low latency, DVR/timeshift, and monetization. They all pull your cache strategy in different directions, and you've got to plan for it.

For sub-3-second latency, standard 4-6 second HLS segments won't cut it. You're moving to CMAF chunks with LL-HLS or LL-DASH, where you're pushing 200-500ms partial segments. Your CDN has to support chunked transfer encoding and HTTP/2 push or preload hints correctly — otherwise you'll add 800ms just in edge buffering. If you're heading down this path, read our deep dive on Low-Latency DASH with CMAF Chunks: Achieving Near-Real-Time CDN Delivery — it covers chunk duration tuning and why your origin-to-shield connection must stay warm with persistent connections. We see latency drop from 18-22s down to 2.8-3.5s when chunking is configured right, but only if your packager and CDN chunk-transfer settings match exactly.

The other two headaches are DVR windows and ad insertion. Viewers expect to pause and rewind a live stream now, which means your edge needs to hold a sliding window — typically 2-4 hours — of segments without evicting them under pressure. That’s a different eviction policy than your normal LRU, and we cover the implementation in CDN DVR Functionality at the Edge: Time-Shifted Viewing for Live Streams. For monetization, client-side ads will get blocked and will stall your player on every ad break. The reliable fix is stitching at the edge/manifest level; our breakdown of Server-Side Ad Insertion (SSAI) Through CDN: Seamless Monetization Without Ad Blockers shows how to manipulate manifests per-viewer without destroying your cache hit ratio — hint: don’t cache personalized manifests at the edge, cache the ad segments and stitch via edge workers. When troubleshooting, always check these first: 1) Are your segments actually aligned across renditions? 2) Is your manifest returning 304s correctly? 3) Did SSAI introduce discontinuity tags that break your player’s buffer?

Frequently Asked Questions

Why is my live stream buffering even with a 99% cache hit ratio?

Hit ratio isn't the whole story for live. If your manifest TTL is too long (say, 10s), players are working off stale playlists and requesting segments that have already rolled off the window, causing 404s and rebuffering. Or your ABR ladder has misaligned GOPs — if your 1080p and 720p renditions don't have IDR frames at the exact same 2-second boundaries, switching renditions forces the player to dump its buffer. Check your transcoder's keyframe interval first, then drop manifest TTL to 1-2 seconds.

How many renditions do I actually need for a live ABR ladder?

Don't overdo it. For most events, 5 renditions is the sweet spot: 1080p @ 4.5 Mbps, 720p @ 2.8 Mbps, 480p @ 1.2 Mbps, 360p @ 750 Kbps, and 240p @ 350 Kbps audio-only fallback. That's enough to cover 95% of devices and networks without exploding your transcoding bill. We've seen customers run 8-10 renditions and pay 40% more for encoding with zero improvement in QoE — your player will only use 3-4 of them anyway. Add a 144p @ 200 Kbps only if you're targeting emerging markets on 3G.

Should I use LL-HLS or Low-Latency DASH for sub-3-second delivery?

It depends on your audience. LL-HLS has better support on iOS/Safari and is easier if you're already on HLS, but it requires HTTP/2 and careful tuning of part durations (we use 200ms parts with 1s segments). Low-Latency DASH with CMAF is more flexible for multi-CDN and works great on Chrome/Edge/Android, but player support is more fragmented. If you need both, package once to CMAF and serve both LL-HLS and LL-DASH from the same chunks — don't run two separate packaging pipelines or you'll double your origin load.

How long should my DVR window be at the edge?

We recommend 2 hours minimum, 4 hours if you can afford the storage. Anything less than 60 minutes frustrates viewers who pause for a phone call, and anything over 6 hours starts to look like VOD and will thrash your cache. Set your edge to cache DVR segments with a 4-hour TTL and a separate cache tier or namespace so live DVR doesn't evict your most popular VOD assets. And make sure your origin DVR store (usually S3/GCS) is the source of truth — the edge is just a window.

Related Articles

References & Standards: HTTP Working Group Specifications · web.dev Articles · IETF Datatracker