A customer’s website is offline. They did the DNS change we asked for, they know DNS “takes time to propagate”, so they wait. A day, sometimes two, refreshing and being patient like the instructions said.
Except waiting will never fix their site, because propagation isn’t the problem. An SSL certificate needs to be reissued on our side, and until someone notices, both of us are politely waiting on nobody.
That’s one ticket out of roughly forty my domain migration generated in six weeks. The migration read as simple on paper: move thousands of customer domains onto Cloudflare, batch by batch, canary first. This post is about what those forty tickets taught me, because nearly every small batch unearthed some unexpected, unique nuance, and it took me embarrassingly long to see what they had in common.
the greatest hits
A sample from the collection. Staff screens showing “Awaiting DNS” on domains that had already migrated, because the status was a stored guess from record-creation time that nobody recomputed. Bulk cleanup that left old certificate renewals silently running against retired infrastructure. New customer domains that could fail setup without anyone, including the program, noticing. A mobile-only DNS resolution error that existed on exactly one browser. A secondary domain whose redirect quietly stopped while its primary worked fine. An automation worker that stalled for 22 hours and nothing complained.
Each one got a ticket, a fix, sometimes a staff doc update. Forty times.
what they actually were
Somewhere around the thirtieth ticket the pattern stopped hiding: these weren’t forty bugs. They were forty missing states in a state machine nobody had written down.
A migrating domain lives in a lattice of partial truths: DNS moved or not, certificate issued or pending or needs-reissue, origin answering or not, redirects intact or broken, cleanup done or half-done. Once I started writing the states down, most of the forty tickets collapsed into a handful of rows:
| domain state | live check that defines it | next action | owner |
|---|---|---|---|
| DNS not moved | nameservers still at old registrar | update DNS records | customer |
| cert pending | DNS moved, no valid cert at the Cloudflare edge | reissue the certificate | us |
| origin dark | cert valid, origin returns 5xx | fix the origin binding | us |
| redirect broken | primary resolves, secondary 404s | repoint the redirect | us |
| cleanup half-done | site live, old renewal still firing | retire the old cert job | automation |
Three audiences were each inferring their own version of that lattice. Staff read screens that guessed. Customers read emails written before their specific situation existed. The automation read database flags set by whatever step ran last.
When all three agree, nobody notices the machinery. When they diverge, you get the customer patiently waiting for propagation while the real state, visible to no one, is “cert needs reissue, our move.”
the fix is a sentence
The canonical state must be computed from live checks, and every state gets exactly one next action and one owner.
Live checks, because the stored answer drifts the moment the world moves and a live answer cannot: where does DNS actually resolve, is the cert actually valid, does the origin actually answer. One next action with one owner, because the entire failure mode is two parties each believing it’s the other’s turn. “Waiting on you: update these records” and “waiting on us: reissuing your certificate” are different sentences, and showing the right one is the whole game.
Staff tools, customer status pages, and the worker all read that same state. Training stops being a document that drifts and becomes mostly a matter of trusting the board. The stuck-domain detector falls out for free: any domain sitting in any state longer than that state should take is a page to a human, which is exactly the alarm that would have caught the cert-reissue customer on hour one instead of day two.
I’ll be honest about where I am. Pieces of this exist: the board reads live DNS checks now, and several screens got rewired from stored status to live verification. The full consolidation is still queued, sitting behind the current batch. But the lens already changed how I triage. When the next batch surfaces its inevitable surprise, my first question now is which state we forgot to name.
The next migration I run will start with the state machine on paper, incomplete and wrong in ways the batches will correct. Forty tickets bought me that. Every surprise after this one lands as a row I add instead of a fire I fight, and no customer sits offline waiting out a propagation that was never coming.