Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save cybercussion/4184ce22e25d344428296fa364a973d3 to your computer and use it in GitHub Desktop.

Select an option

Save cybercussion/4184ce22e25d344428296fa364a973d3 to your computer and use it in GitHub Desktop.
Operator lens: Tero Potila music site migration to Cloudflare (council critique)

Operator Lens — Tero Potila Site Migration to Cloudflare

Council role: Operator (run-it-in-prod pressure test) Topic: Music Site and Blog — soundfactory.com / teropotila.com migration off Angular/AWS to Cloudflare Date: 2026-06-20


The thing that will bite you at 3am: R2 has no ops-volume circuit breaker

R2 has no egress fees — that headline is real and load-bearing for the near-$0 goal. But Class-B operations (GET requests) are $0.36/million after the 10M free-tier ops/month. A composer site is one viral sync placement away from 50M GET requests in 48 hours. At Tero's current traffic that is probably theoretical, but the failure mode has no natural circuit breaker. Workers Static Assets sits in front of the static deploy and caches aggressively, but R2-served audio files on a custom subdomain (e.g., media.soundfactory.com) bypass that cache entirely unless you explicitly put a Worker or Cache Rule in front of the bucket. If you ship R2 naked and a film gets licensed and goes press-viral, the ops bill materializes before anyone wakes up.

Ship a Cache Rule or a thin Caching Worker in front of every R2 audio/video path on day one, not as a follow-up task.


Missing signals — what you cannot see without adding them

R2 ops-volume alerting does not exist out of the box. Cloudflare's R2 dashboard shows usage but there is no native budget alert or webhook trigger on ops counts. You need to either (a) wire a scheduled Worker that queries the R2 Analytics API and posts to a Slack channel or email if ops-this-month exceeds a threshold, or (b) accept that you find out at invoice time. For a near-$0 site this is the one bill-surprise vector that matters.

Static-asset cache purge is invisible. When Tero updates a portfolio page or blog post, the deploy pipeline must bust the cache on affected routes or visitors get stale HTML for hours. new.cybercussion.com's deploy pipeline has cache-busting logic (import-map stamping) — carry that pattern forward explicitly. Without it you will get "I pushed the fix but the site still shows the old version" incidents at the worst moment (e.g., the day a film opens).

Contact-form Worker errors are silent by default. The Resend-backed contact Worker has no dead-letter queue. If Resend's free tier (100 emails/day) hits its cap or the API key rotates, form submissions are dropped with no record. Add a D1 write (or KV append) on every form POST so you have a local copy independent of Resend's delivery status. This is one table and ten lines of code — build it into the initial Worker, not as a follow-up.

DNS cutover (VF5) has an invisible failure window. Moving nameservers to a new CF account means the old AWS Angular site goes dark the moment NS propagation completes globally. If anything in the new site is broken — a misconfigured wrangler.toml asset path, a missing 301 for an old Angular route, a CORS header missing on R2 — the SEO equity and any live bookings traffic hits a wall.

The cutover runbook must include:

  1. New site verified live on a staging subdomain under the new CF account before NS flip
  2. 301 map for every Angular route that has inbound links (pull from Search Console / Ahrefs on the old domain first)
  3. A rollback NS plan — lower old AWS TTL to 60s at least 48h before cutover so a rollback propagates fast
  4. Post-cutover smoke: hit every 301 source, every R2 audio URL, the contact form, and confirm Search Console picks up the sitemap within 48h

Emdash v0.1.0 operational risk

If the council recommends Emdash, name what "v0.1.0 in production on a client's live revenue site" means operationally: no community-sourced incident responses, no Stack Overflow answers, no plugin ecosystem for auth/search/image-resize. When Tero's blog breaks at midnight before a film festival, the on-call surface is the Emdash GitHub issues and Cloudflare Discord.

Plain Astro SSG has five years of production deployment history, a large community, and solves F1 (real HTML per route) natively. The operator recommendation is plain Astro unless Tero explicitly opts into bleeding-edge risk. Emdash is a watch-this-space once it exits beta.


Cloudflare Stream vs R2 for video (VF3): make the call before launch, not after the complaint

R2-served progressive MP4 is operationally simpler but gives you no adaptive bitrate, no seek-without-full-download on slow connections, and no play/completion analytics. Cloudflare Stream solves all three but adds a metered line item (~$5/1000 minutes stored + delivery).

The operator concern: R2-only video will generate "video won't load on mobile" support complaints that look like hosting bugs but are actually an unresolved product decision. Make the R2-vs-Stream call explicitly before launch, based on Tero's actual video catalog (sizes, lengths, whether HD showreels are a primary client-facing asset). If showreels are short clips under 2 minutes, R2 progressive is fine. If Tero has 10-minute HD film scores, Stream is the right call and the cost is predictable and small.


Summary: the non-negotiable day-one items

Item Why it can't wait
Cache Rule / Caching Worker in front of R2 media paths Only bill-surprise vector with no natural ceiling
D1/KV write on every contact-form POST Silent Resend failure = lost client inquiry, no recovery
Staging subdomain smoke before NS cutover Rolling back a broken cutover costs SEO equity and trust
301 map from all Angular routes with inbound links SEO equity loss is permanent if links 404 on launch day

Everything else — llms.txt, JSON-LD depth, Emdash vs Astro, Stream vs R2 — has a recoverable failure path. These four do not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment