CDN Implementation Plan
Context
Section titled “Context”The cluster hosts roughly 20 brochure/marketing sites for corporate clients. These are static or near-static content served through Traefik. The origin cluster runs in a single zone (zonal GKE or DOKS), so a CDN provides the availability safety net that makes regional redundancy unnecessary for this workload.
Why Cloudflare free tier
Section titled “Why Cloudflare free tier”Cloudflare’s free plan covers everything these sites need at $0/domain:
- Edge caching: static assets served from 300+ PoPs worldwide
- DDoS protection: automatic L3/L4/L7 mitigation
- SSL termination: universal SSL certificates, no ACME configuration needed per domain
- Always Online: serves cached pages when the origin is unreachable (zone outage, maintenance)
Alternatives considered:
- GCP Cloud CDN ($5-15/mo, GKE only, no WAF/DDoS): vendor-locked, fewer features, costs more
- DigitalOcean: no CDN product for HTTP traffic
- Regional GKE ($73/mo control plane + 3x node cost): solves availability at 4-5x the cost, does not help with latency or DDoS
Architecture
Section titled “Architecture”Client -> Cloudflare edge (cached) -> Cluster LB -> Traefik -> Service -> PodCloudflare operates in proxy mode (orange cloud). DNS records point each domain to the cluster’s load balancer IP. Cloudflare terminates TLS at the edge and forwards traffic to the origin over HTTPS.
For brochure sites with infrequent updates, cache TTLs can be set to hours or days. During an origin outage, Cloudflare serves stale cached content, so clients see no downtime.
OpenTofu module
Section titled “OpenTofu module”A new module at tofu/remote/cloudflare/ will manage:
- Cloudflare zones: one per client domain
- DNS A records: root and
wwwpointing to the cluster LB IP (output from DO or GKE module) - Page rules or cache rules: set cache TTL for static content
Variables
Section titled “Variables”| Variable | Type | Description |
|---|---|---|
domains | list(string) | Client domain names (e.g. ["example.com", "acme.co"]) |
origin_ip | string | Cluster load balancer IP (from tofu output of active provider) |
cloudflare_api_token | string | API token with Zone:Edit and DNS:Edit permissions |
Required provider
Section titled “Required provider”terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" version = "~> 5.0" } }}Secrets
Section titled “Secrets”The Cloudflare API token goes in .env.json (SOPS-encrypted), extracted at runtime the same way DO and GCP credentials are handled:
export CLOUDFLARE_API_TOKEN=$(sops -d --extract '["CLOUDFLARE_API_TOKEN"]' .env.json)Cost impact
Section titled “Cost impact”| Component | Monthly cost |
|---|---|
| Cloudflare free (20 zones) | $0 |
| Cloudflare Pro (20 zones, if needed) | $400 |
Free tier is sufficient for brochure sites. Pro adds WAF rules and image optimization; upgrade per-domain if a client requires it.
Blockers
Section titled “Blockers”- Domain list: need the ~20 client domain names before creating zones
- Cloudflare account: need an account with the domains added (nameserver delegation)
- API token: generate a token scoped to Zone:Edit + DNS:Edit, add to
.env.json
Implementation steps
Section titled “Implementation steps”- Receive client domain list
- Create Cloudflare account, add domains, delegate nameservers
- Generate API token, add to
.env.jsonviasops - Create
tofu/remote/cloudflare/tofu module - Add
tofu:cf:checkandtofu:cf:costmise tasks - Wire
origin_ipfrom the active provider’s output into the Cloudflare module - Deploy and verify each domain resolves through Cloudflare