SRE Practices for CDN: Applying Site Reliability Engineering to Content Delivery

Back in 2019 we shipped what looked like a harmless cache-key normalization at 2 p.m. on a Tuesday. Within twenty minutes our Frankfurt edge hit ratio fell from 94% to 31%, our origins started shedding connections, and checkout pages timed out for one of our largest retail customers. That was the afternoon our team stopped treating CDN operations like a traditional NOC and started applying site reliability engineering for real. If you've run a CDN at any serious scale, you've felt that same drop in your stomach.

Why Traditional CDN Operations Can't Keep Up With Edge Scale

For years, most CDN teams operated on a ticket-and-alert model. You'd get paged for origin 5xx spikes, you'd manually purge, you'd SSH into a PoP and restart ATS or Varnish, and you'd write a post-mortem. That works when you have twenty PoPs and a few hundred customers. It doesn't work when you have 250+ PoPs, thousands of configs deployed daily, and traffic that can double in minutes because a streaming event went viral. I've watched teams burn out trying to keep up with that volume by just hiring more ops engineers. You don't scale that way.

The core tension with SRE CDN work is that CDNs look deceptively simple from the outside - cache static assets, proxy dynamic traffic - but the failure modes are brutal. A bad regex in a VCL, a stale TLS cert, an inconsistent purge queue, and suddenly you don't have one broken server, you have a globally replicated bad decision. Traditional ops focuses on mean time to repair. SRE practices force you to focus on mean time to detect bad changes before they replicate, and on building systems that don't need a human to flip a switch at 3 a.m. That shift from reactive to engineered reliability is what separates teams that constantly fight fires from teams that actually control their error budgets.

That doesn't mean you throw away everything your network operations centre learned. You codify it. We started by taking our top five most frequent pages - cache invalidation delays, origin health flaps, certificate renewal failures, config deployment drift, and log delivery lag - and asking which ones required human judgment and which were just undifferentiated work. You'd be surprised how many were the latter. Our work on CDN Toil Reduction Through Automation: Eliminating Repetitive Operations Work came directly from that audit, but we also had to be careful not to automate ourselves into a new class of outage, which is exactly The CDN Automation Reliability Paradox: When Automation Causes More Outages that every senior team hits around year two of their automation journey.

# SLO burn-rate alerting for CDN edge availability
# Based on 99.95% availability SLO over 28 days
groups:
- name: cdn-slo-burn
  rules:
  - alert: CDNHighBurnRate
    expr: |
      (
        sum(rate(cdn_edge_requests_failed_total[5m]))
        /
        sum(rate(cdn_edge_requests_total[5m]))
      ) > (0.0005 * 14.4) # 14.4x burn for 2% budget in 1 hour
    for: 5m
    labels:
      severity: critical
      tier: edge
    annotations:
      summary: "CDN edge burn rate is 14.4x - error budget will exhaust in ~2 hours"
      runbook: "https://runbooks.internal/cdn/burn-rate"

  - alert: CDNLowBurnRate
    expr: |
      (
        sum(rate(cdn_edge_requests_failed_total[1h]))
        /
        sum(rate(cdn_edge_requests_total[1h]))
      ) > (0.0005 * 6) # 6x burn - slower, needs investigation
    for: 15m
    labels:
      severity: warning
    annotations:
      summary: "CDN edge burn rate is 6x - investigate origin and cache layer"

Defining Service Level Objectives That Match How Users Experience the CDN

A lot of teams copy-paste availability SLOs from their origin services and call it CDN reliability. That's a mistake. Users don't care if your edge daemons are up; they care if their content is fresh, fast, and actually served. When we rebuilt our SLOs, we threw out simple "edge uptime" and replaced it with four service level indicators that map to real pain: successful request rate as measured at the edge, cache hit ratio for cacheable content, time to first byte at the 95th percentile from real user monitoring, and origin offload failure rate. If you're not measuring from the client perspective, you're measuring the wrong thing. The web.dev Articles on Core Web Vitals and the HTTP Working Group Specifications on caching semantics were surprisingly useful references when we were arguing about what "successful" even meant - does a stale-while-revalidate hit count as a success? Does a 304 count?

Here's what I learned the hard way: don't set your SLO target based on what marketing wants to promise. Set it based on what you can actually measure and what your current system delivers. We collected 90 days of RUM and edge log data before we set anything. Our edge request success was hovering around 99.97% already, so promising 99.95% gave us a realistic 21 minutes of error budget per 28 days without lying to customers. But our P95 TTFB was 180ms globally, so we couldn't in good conscience set an SLO of 100ms - we'd burn that budget every day in Southeast Asia. We set it at 220ms initially, then used performance work to pull it down to 140ms over two quarters. That honesty builds trust with product teams, because they know when you say you're out of budget, you mean it.

You also need to treat your SLOs as code. If your SLO definitions live in a Google Doc, they don't exist. We store ours as versioned configs that generate both dashboards and alerting rules, so there's no drift between what we report and what pages us. That approach lines up closely with proper CDN Error Budget Management: Balancing Reliability with Feature Velocity, and it's saved us more than once when someone wanted to "temporarily" lower a threshold to ship a feature.

#!/usr/bin/env python3
# safe_config_rollout.py - Canary CDN config to 5% of PoPs with automatic rollback
import time
from cdn_api import deploy_config, get_error_rate, rollback

CANARY_POPS = ["fra02", "iad03", "sin01"]
ERROR_THRESHOLD = 0.002  # 0.2% error rate
EVALUATION_WINDOW = 300  # 5 minutes

def canary_deploy(config_version):
    print(f"Deploying config {config_version} to canary PoPs: {CANARY_POPS}")
    deploy_config(version=config_version, pops=CANARY_POPS, strategy="canary")

    time.sleep(EVALUATION_WINDOW)

    for pop in CANARY_POPS:
        rate = get_error_rate(pop=pop, window="5m")
        print(f"[{pop}] error rate: {rate:.4f}")
        if rate > ERROR_THRESHOLD:
            print(f"Threshold breached in {pop}, rolling back globally")
            rollback(version=config_version)
            return False

    print("Canary healthy, proceeding to full fleet rollout")
    deploy_config(version=config_version, pops="all", strategy="staged")
    return True

if __name__ == "__main__":
    canary_deploy(config_version="v2024.11.42")

Error Budgets and Toil Budgets That Keep CDN Teams Honest

An error budget isn't just a number you put on a dashboard. It's a decision-making tool. When we introduced ours, the biggest pushback wasn't from engineering, it was from product. Suddenly, "we've burned 80% of our budget this month, so we need to freeze non-critical purges and config changes" wasn't an engineering preference, it was policy. That policy only works if everyone agrees on it before an incident. We run a monthly review where we look at budget burn, classify it - was it a bad deployment, an origin issue, an upstream provider, or just normal noise? - and then decide if we need to adjust velocity or invest in reliability work. Without that ritual, site reliability engineering just becomes a new title for the same old on-call rotation.

We pair error budgets with toil budgets, something I don't see enough CDN teams talk about. We cap toil - manual, repetitive, automatable work - at 30% of a team's time, max 40% in a bad week. If you're spending more than that hand-holding cache purges, rotating certs manually, or babysitting log pipelines, you aren't doing SRE work, you're doing ops with a fancier name. We track toil in our sprint board with a specific label, and if we hit 35% for two sprints in a row, we stop feature work and automate the biggest toil source. That discipline is how we cut our manual purge-related pages from 12 per week to less than one per month.

Service Level Indicator SLO Target (28-day window)

Putting SRE Into Practice: Automating SLOs From Edge to Origin

Theory's great, but reliability doesn't improve until you codify it. For CDN teams, that means moving SLOs out of Google Docs and into your monitoring and delivery pipeline. We've found the most effective starting point is instrumenting what actually matters at the edge: not just origin availability, but cache hit ratio, time-to-first-byte (TTFB) at the PoP, and revalidation latency. If your SLIs are only measured at the origin, you're blind to the 30-40% of user experience that's dictated entirely by edge behavior.

Once your SLIs are emitting, tie them directly to automation. Every CDN config change — whether it's a VCL update, a new shielding rule, or a TLS cert rotation — should go through a canary. We typically roll to 5% of PoPs (usually 8-12 low-traffic edges first), burn for 15 minutes, and automatically compare p50/p95 latency and 5xx rates against the SLO baseline. If burn rate exceeds 2x for more than 5 minutes, the pipeline rolls back without human intervention. It’s not glamorous, but it’s saved us from three global config pushes that would have tanked hit ratios by 12-18% last quarter alone.

This is where CDN Runbook Automation: Converting Manual Procedures into Executable Scripts becomes non-negotiable. If your runbook says “check cache status, then purge if needed,” that’s not a runbook — that’s toil. Convert it into a script that checks your SLI dashboard, validates the error budget, and executes via API. The goal is that on-call isn’t making decisions at 2 a.m.; the system is.

# Prometheus SLO burn-rate alert for CDN edge latency
# SLO: 99.9% of requests serve TTFB < 120ms over 28-day window
groups:
- name: cdn-slo-latency
  rules:
  - record: slo:cdn_ttfb_burn_rate_5m
    expr: |
      (
        sum(rate(cdn_edge_requests_total{ttfb_ms=">120"}[5m]))
        /
        sum(rate(cdn_edge_requests_total[5m]))
      ) / (1 - 0.999)

  - alert: CDNHighBurnRate
    expr: slo:cdn_ttfb_burn_rate_5m > 14.4
    for: 5m
    labels:
      severity: critical
      burn_rate: "14x"
    annotations:
      summary: "CDN latency SLO burning too fast (14x) - auto-rollback canary"
      runbook: "/runbooks/cdn-latency-burn"

Advanced Patterns: Graceful Degradation and Taming Edge Failures

At scale, you don't prevent failures — you isolate them. The best CDN SRE teams design for partial degradation, not perfect uptime. That means implementing origin shielding with active load shedding. When your origin latency spikes past 800ms, your edge shouldn't dogpile it with retries. We use a circuit-breaker pattern at the shield PoP: if origin error rate hits 15% over a 60-second window, we serve stale for up to 300 seconds while capping origin concurrency to 20% of normal. Users get a slightly stale asset instead of a 503, and you preserve your error budget. We’ve seen this cut origin-induced outages by 70%.

Another pattern we rely on heavily is chaos testing for cache consistency. It’s easy to validate happy-path purging; it’s harder to know what happens when a purge propagates to 320 PoPs and 4 of them are partitioned. We run weekly experiments that intentionally blackhole 10% of our edge nodes and inject 500ms of latency between shield and origin. Last month this surfaced a bug where our tiered cache was revalidating with `If-None-Match` incorrectly, causing a 2.4% miss storm on every deploy. Without that test, we wouldn't have found it until it hit production traffic at peak.

When things do break — and they will — how you respond matters more than the fix itself. Don't just restore service; capture the system dynamics that allowed the failure. A solid CDN Postmortem Analysis: Learning from Incidents to Prevent Recurrence should focus on detection time, mitigation time, and which SLO was actually breached, not just root cause. And none of that works without Building a Blameless CDN Operations Culture: Focus on Systems, Not People. If engineers are afraid to trigger a rollback because they'll be blamed for burning error budget, your culture is your biggest reliability risk.

Frequently Asked Questions

What’s a realistic SLO for a global CDN serving dynamic and static content?

Don't set one SLO for everything. For static assets (images, JS, CSS), aim for 99.95% availability with p95 TTFB < 80ms and cache hit ratio > 92% at the edge. For dynamic/API acceleration, lower the expectation: 99.9% availability with p95 origin-to-edge latency < 250ms is more honest. Measure them separately — blending them hides where you're actually failing.

How do you handle error budgets when product wants to push a risky edge compute feature?

Let the budget decide. If you've burned 60% of your monthly error budget in the first two weeks, you freeze non-critical edge worker deploys — no debate. We plot remaining budget on the sprint board. If product absolutely must ship, they need to buy back budget by investing in reliability work first: e.g., adding stale-while-revalidate or fixing that flaky purge queue. It turns reliability from a "no" into a negotiation.

Our MTTR is still over 45 minutes despite alerts. What are we missing?

You're likely alerting on symptoms, not SLO burn. Switch to multi-window burn-rate alerts (5m and 30m windows) and tie them to executable runbooks. If an alert doesn't have an automated diagnostic script attached — one that checks PoP health, shield status, and origin saturation in under 30 seconds — it's just noise. Most teams cut MTTR to under 12 minutes once they stop SSHing into boxes and start executing pre-approved scripts.

Should we SRE-ify our CDN vendor or just our origin?

Both, but you own the integration. Even with a managed CDN, you're responsible for defining SLIs that reflect user experience and holding the vendor to them via your own synthetic monitoring (e.g., Catchpoint or ThousandEyes from 50+ cities). We require our vendor to expose real-time logs and cache status headers so we can calculate our own hit ratio and latency SLIs — don't trust the dashboard alone.

Related Articles