The technical half of putting Cloudflare in front of a 20-year-old platform took days. My zones, my certificates, my origin config: batch after batch, scripted, done. If that had been the whole project, this post would be a victory lap.

The other half is thousands of customer domains, and it’s still going. As usual, the project was way more complex than I anticipated, and the complexity wasn’t anywhere I expected it to be.

the part nobody blogs about

Our customers can point their own domains at their sites. Which means their DNS lives in their registrar accounts, managed by them. League volunteers, club treasurers, someone’s nephew who set it up in 2014. When the platform moves behind Cloudflare, every one of those domains needs its records changed by a person I can’t log in as, on a timeline I can’t control, for a migration they never asked for.

No script fixes that. The technical migration was days of work. Shepherding thousands of strangers through a change on registrars I can’t log into is the actual project.

what the campaign actually looks like

The pieces that made it tractable, in the order I built them:

A canary batch first. A handful of domains moved onto the new path and watched, because the first thing a migration teaches you is what you got wrong, and you want that lesson at a blast radius of five domains, not five thousand.

Bulk migration of everything I control. All company-managed domains moved in scripted batches. AI agents ran the batches while I did other work; each batch verified itself before the next started. What I control moves fast. That’s exactly why the rest is the project.

A live migration board. Every customer-managed domain, its current state, what it’s waiting on. Automated checks notice when a customer’s DNS change actually lands, so the board updates itself instead of relying on people telling me they did the thing. Half of them wouldn’t, even after doing it.

Redacted, one slice of it looks like this. Domains move in batches of 50, and the board is the source of truth for which ones are safe to touch next.

domain (redacted)statewaiting ondays
****league.orgRELAY_ACTIVEcustomer DNS change22
****club.comEMAIL_2_SENTcustomer to act9
****sports.netCERT_REISSUEACME retry1
****youth.orgMIGRATEDnothingdone

The days column is the one a practitioner would check me on. A row that has sat in RELAY_ACTIVE for three weeks is a customer who is never going to move on their own, and it’s the whole reason the relay in the next section has to be permanent.

Deadline emails, staged. First notice, then a second warning with a date. Writing these is its own skill: the audience doesn’t know what DNS is, and the email has to get them to either act or forward it to whoever can.

An apex relay, so a domain that’s mid-flight or late still works instead of going dark. Which meant the relay itself became production infrastructure, so it got monitoring, alerting, and a backup with a restore I actually ran end to end. A fallback you have never restored from is a wish, not a fallback.

And even with all that, nearly every batch unearthed some nuance nobody predicted: a site “waiting for propagation” that actually needed a certificate reissued, staff screens confidently showing a stale state, cleanup jobs leaving old renewals running. Those forty tickets turned out to share one root cause, and they earned their own post about the state machine I should have written first.

why bother, beyond the crisis

The trigger for all this was bot swarms, which is its own post. But the durable win is different: every pointer to my production server now resolves through Cloudflare. When I eventually move that server, and I will have to, thousands of customer DNS configs don’t need to change again. The next migration touches infrastructure only, no long tail, no email campaign. One layer of indirection, bought once, paid for by exactly one round of this pain.

The long tail takes months no matter how urgent the trigger felt. Mine started as bot swarms and became a campaign I’m still running. The board is open in a tab as I write this, and a handful of rows have sat amber for weeks, waiting on a volunteer who hasn’t opened the second email. That’s the part nobody blogs about, and it’s the part that’s still going.