GitOps for CDN Infrastructure: Version-Controlled Edge Configuration

· Sarah Mitchell · 9 min read

CDN configurations are infrastructure. They control how millions of requests route, cache, and transform at the edge every second. Yet many teams still manage CDN settings through web dashboards — clicking through forms, hoping the person who made the last change documented it somewhere. GitOps brings the same discipline to CDN infrastructure that modern teams already apply to application code: every change is a commit, every deployment is a merge, and the live state always matches what's in the repository. This article covers how to implement GitOps workflows for CDN infrastructure using declarative configuration, pull-based reconciliation, and automated drift detection.

The Problem with Dashboard-Driven CDN Management

CDN providers offer polished dashboards for managing edge configuration. They're convenient for exploring features and making quick changes. They're also a liability in production. Dashboard changes leave no audit trail beyond the provider's own logs (which may not capture who changed what). They can't be peer-reviewed before deployment. They can't be tested in a staging environment. And when something breaks at 3 AM, the only way to understand what changed is to compare the current state against someone's memory of what it used to be.

Consider a typical incident: a cache rule change deployed through the dashboard causes origin overload because the new rule bypasses caching for a high-traffic path. Without version control, answering "what changed?" requires digging through the CDN provider's audit logs, correlating timestamps, and manually reconstructing the previous configuration. With GitOps, you run git log and see exactly what changed, who approved it, and when — then git revert to restore the previous state.

GitOps Principles Applied to CDN

GitOps is built on four principles, each mapping directly to CDN infrastructure management:

Performance Comparison: Edge vs Origin Processing
Metric Edge Processing Origin Processing Improvement
Latency (p50)8–15 ms120–300 ms15–20x faster
Cache Hit Ratio85–95%N/AReduces origin load
TLS Handshake1–5 ms (nearby PoP)50–150 ms10–30x faster
Bandwidth Cost$0.01–0.04/GB$0.05–0.12/GB3–5x cheaper
Availability99.99% (multi-PoP)99.9% (single DC)10x fewer outages

Declarative configuration. CDN state is described as data — YAML, HCL, JSON — not as a sequence of API calls. The configuration declares what the edge should look like, not the steps to get there. This means the configuration is idempotent: applying it multiple times produces the same result, regardless of the current state.

Version controlled. All configuration lives in a Git repository. Every change is a commit with an author, timestamp, and message. Feature branches and pull requests provide a natural workflow for proposing, reviewing, and approving changes before they reach production.

Automatically applied. Approved changes are applied to the live CDN by an automated agent — not by a human running commands or clicking buttons. This eliminates the gap between "approved" and "deployed" that causes configuration drift.

Continuously reconciled. A reconciliation loop compares the desired state in Git against the actual state at the CDN provider. If they diverge — from manual dashboard changes, partial API failures, or provider-side issues — the reconciler corrects the live state to match Git.

GitOps CDN Reconciliation Loop Git Repository Desired State cdn-config.yaml edge-rules.hcl Reconciler Compare & Apply Runs every 60s CDN Provider Actual State Edge Rules Cache Policies Pull Request Review & Approve terraform plan output Drift Alert Manual change detected Auto-remediate Pull desired Apply changes Read actual
Figure 1: GitOps Reconciliation Loop — the reconciler continuously pulls desired state from Git and corrects live CDN configuration

Repository Structure

Organize your CDN configuration repository around environments and concerns. Separate base configurations from environment-specific overrides, and isolate different types of edge logic into dedicated directories.

cdn-infrastructure/
├── environments/
│   ├── production/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   ├── staging/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   └── development/
│       └── ...
├── modules/
│   ├── origin-pool/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── cache-rules/
│   │   └── ...
│   ├── waf-rules/
│   │   └── ...
│   └── edge-functions/
│       └── ...
├── edge-functions/
│   ├── src/
│   │   ├── routing.js
│   │   ├── auth.js
│   │   └── transforms.js
│   ├── tests/
│   │   └── routing.test.js
│   └── wrangler.toml
├── policies/
│   ├── cache-policies.yaml
│   ├── security-headers.yaml
│   └── rate-limits.yaml
└── .github/
    └── workflows/
        ├── plan.yaml
        └── apply.yaml

This structure separates infrastructure definitions (Terraform modules) from business logic (edge functions) and policy declarations (YAML configs). Each can evolve independently, reviewed by different teams with different expertise.

Pull Request Workflow

Every CDN change follows the same workflow: branch, change, plan, review, merge, apply. The critical addition compared to standard CI/CD is the plan step — before any change reaches production, the automation generates a diff showing exactly what will change at the CDN provider.

# .github/workflows/plan.yaml
name: CDN Config Plan
on:
  pull_request:
    paths:
      - 'environments/**'
      - 'modules/**'
      - 'policies/**'

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        working-directory: environments/production
        run: terraform init

      - name: Terraform Plan
        working-directory: environments/production
        run: terraform plan -no-color -out=plan.tfplan
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}

      - name: Post Plan to PR
        uses: actions/github-script@v7
        with:
          script: |
            const plan = require('fs').readFileSync(
              'environments/production/plan.stdout', 'utf8'
            );
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## CDN Configuration Plan\n\`\`\`\n${plan}\n\`\`\``
            });

The plan output shows reviewers exactly which CDN resources will be created, modified, or destroyed. Reviewers can assess the blast radius before approving. For security-sensitive changes like WAF rule modifications, require additional approvals from the security team.

Drift Detection and Remediation

Drift occurs when the live CDN state diverges from Git — typically because someone made a manual change through the dashboard or API. Without drift detection, these undocumented changes accumulate until the next GitOps apply overwrites them, potentially breaking production.

# drift-detector.py
import subprocess
import json
import requests

def detect_drift():
    result = subprocess.run(
        ["terraform", "plan", "-detailed-exitcode", "-json"],
        capture_output=True, text=True,
        cwd="environments/production"
    )

    # Exit code 2 = changes detected (drift)
    if result.returncode == 2:
        changes = parse_plan_output(result.stdout)
        return {
            "drifted": True,
            "resources": changes,
            "timestamp": datetime.utcnow().isoformat()
        }

    return {"drifted": False}


def handle_drift(drift_report):
    if not drift_report["drifted"]:
        return

    for resource in drift_report["resources"]:
        if resource["action"] == "update":
            # Non-destructive drift: auto-remediate
            if is_safe_to_auto_fix(resource):
                apply_fix(resource)
                notify_team(f"Auto-fixed drift: {resource['address']}")
            else:
                # Destructive or ambiguous: alert for manual review
                create_drift_issue(resource)
                notify_team(
                    f"DRIFT DETECTED: {resource['address']} "
                    f"requires manual review"
                )


def is_safe_to_auto_fix(resource):
    safe_resources = [
        "cloudflare_page_rule",
        "cloudflare_rate_limit",
        "cloudflare_filter",
    ]
    return resource["type"] in safe_resources

Run drift detection on a schedule — every 5-15 minutes for critical CDN configurations, hourly for less sensitive settings. The key decision is the remediation policy: which drift is safe to auto-fix (restoring Git's desired state) versus which requires human review (the manual change may have been an emergency fix that should be committed to Git instead).

Multi-CDN GitOps

For organizations using multiple CDN providers, GitOps provides a unified management layer. Define an abstraction format that captures intent without provider-specific syntax, then use provider adapters to translate the abstract configuration into each provider's native format.

# policies/cache-policies.yaml (provider-neutral)
cache_rules:
  - name: static-assets
    match:
      path_patterns:
        - "*.css"
        - "*.js"
        - "*.woff2"
        - "*.svg"
    behavior:
      cache_ttl: 31536000  # 1 year
      browser_ttl: 86400   # 1 day
      stale_while_revalidate: 3600
      stale_if_error: 86400

  - name: api-responses
    match:
      path_prefix: "/api/"
      methods: ["GET"]
    behavior:
      cache_ttl: 60
      vary_on:
        - "Accept"
        - "Authorization"
      bypass_on:
        - cookie: "session_*"

  - name: html-pages
    match:
      content_type: "text/html"
    behavior:
      cache_ttl: 300
      stale_while_revalidate: 60
      cache_key_includes:
        - query_string
        - accept_encoding

The adapter layer translates this into Cloudflare Page Rules, Fastly VCL snippets, or CloudFront cache behaviors. When you change a policy in the YAML, all providers are updated consistently through their respective reconciliation loops.

Edge Function Versioning

Edge functions — Cloudflare Workers, Lambda@Edge, Fastly Compute — are code, and they deserve the same GitOps treatment as infrastructure configuration. Version edge functions alongside CDN configuration, test them in CI, and deploy them through the same pull request workflow.

# wrangler.toml (Cloudflare Workers configuration)
name = "edge-router"
main = "src/routing.js"
compatibility_date = "2026-09-01"

[env.staging]
name = "edge-router-staging"
routes = [
  { pattern = "staging.example.com/*", zone_name = "example.com" }
]

[env.production]
name = "edge-router-production"
routes = [
  { pattern = "example.com/*", zone_name = "example.com" },
  { pattern = "www.example.com/*", zone_name = "example.com" }
]

[build]
command = "npm run build"

[observability]
enabled = true

Pin the compatibility_date to prevent the Workers runtime from changing behavior under your code. Update it deliberately when you're ready to adopt new platform features, and test against the new date in staging before promoting to production.

Rollback Strategies

GitOps makes rollback trivial in theory — revert the commit and let the reconciler apply the previous state. In practice, CDN rollbacks have nuances that require careful handling.

Configuration rollbacks are straightforward. Revert the Terraform state to a previous version and apply. The reconciler detects the difference between the live state and the reverted desired state, then applies the changes. For CDN configurations that propagate across edge PoPs, expect a 5-30 second window where some PoPs serve the new configuration while others still serve the old one.

Edge function rollbacks are trickier because function deployments may involve compiled artifacts. Keep previous deployment artifacts in a registry or artifact store so that reverting the Git commit also restores the previous function binary, not just the source code. Most CDN providers maintain previous deployments internally and support instant rollback through their API.

Integrate rollback automation with your monitoring system. If key metrics degrade after a GitOps apply, automatically revert the last commit and re-apply the previous state. This creates a fully automated deploy-monitor-rollback loop that responds faster than any human operator.

Security Considerations

Implementation Checklist

PhaseActionTool
FoundationImport existing CDN config into Terraform stateterraform import
FoundationSet up repository structure with modulesGit + Terraform
WorkflowConfigure PR-based plan-and-apply pipelineGitHub Actions / Atlantis
WorkflowAdd staging environment for pre-production testingTerraform workspaces
SafetyImplement drift detection on a scheduleCron + terraform plan
SafetyDefine auto-remediation policiesCustom reconciler
AdvancedAdd edge function CI/CD pipelineWrangler / provider CLI
AdvancedImplement automated rollback triggersMonitoring + reconciler

External References

References & Standards: Cloudflare documentation · OWASP CDN Security