Loading...
Preparing article
Fetching the latest blog content.
Loading...
Fetching the latest blog content.
2026-08-12 • 8 min read
Moving a live 50,000-user production platform off DigitalOcean and onto AWS without a single customer noticing — rebuilt entirely in Terraform and flipped over with one reversible DNS cutover, with the old stack kept warm as a real rollback net.

A zero-downtime DigitalOcean to AWS migration reads like one line on a sprint board. In practice, moving a live production platform — one serving 50,000+ users — off DigitalOcean and onto AWS meant standing up an entire environment in a new cloud and then flipping every one of them onto it without a single one noticing. I was the only DevOps/SRE on it, so there was also nobody to hand the pager to if the cutover went sideways.
The whole project bends around one moment: the DNS cutover — a switch you get to throw exactly once, in public, on live traffic. So the real design goal was never "build AWS." It was build AWS in a way that lets me flip everyone onto it with the old stack still warm behind me — a net I could fall back into if the new floor didn't hold.
The DigitalOcean environment wasn't wrong when it was built. It grew organically — provisioned by hand, one decision at a time, until its shape was whatever the last change left behind. For a platform that size, that had hardened into three liabilities:
The most glaring piece was the network shape: the layers weren't cleanly isolated, so credentials were doing work that network boundaries should have been doing. What we needed was the opposite of organic: a reproducible, multi-AZ, in-region environment where database, secrets, and compute were properly separated — reached without an outage for customers who were mid-transaction while I worked.
Rule one: no click-ops. If the migration was going to be trustworthy, the new environment had to exist as code before it existed as infrastructure. Everything went into Terraform and Ansible — Terraform to describe the AWS footprint, Ansible to configure what ran on it. The payoff isn't tidiness; it's that a reviewed, version-controlled environment can be rebuilt on demand — the same property that makes a rollback survivable.
The architecture:
Segmentation was the theme. In the old world, everything could more or less reach everything. In the new one, each layer — load balancer, compute, cache, secrets, database — is its own boundary, and crossing it is an explicit rule.
If I had to point to the one change that did the most for the security posture, this is it — and it's the highest-value move in almost any cloud migration: make sure the database isn't reachable from the public internet at all. A database whose only defense is credentials is one leaked connection string away from a very bad day.
In the new architecture the database sits behind VPC peering plus an explicit allowlist: it only accepts traffic arriving over a private peering connection from a named set of sources, and nothing else can even open a socket. Credentials become the second line of defense instead of the only one — exactly the control an auditor wants to see, rather than a public endpoint gated by a password.
This one you only learn by watching a system fail. An ALB decides whether to keep sending traffic to an instance by hitting a health endpoint — and if that endpoint fails, it pulls the node out of rotation. The check had drifted into reporting the message broker's state, which meant a transient queue hiccup — something customers would never have felt — could mark healthy instances unhealthy and yank the whole fleet out from under the load balancer.
So I decoupled the health signal from the broker. The check now answers one question only: can this instance serve requests? A backed-up queue is a queue problem, not an availability outage — and the load balancer should never confuse the two.
This is the moment the whole project was built around. The mechanics are unforgiving: DNS is not instant. A record's TTL decides how long resolvers keep serving the old answer, so for a window of time both values are in the wild — some customers resolving to AWS, some still to DigitalOcean. You don't cut traffic over; you drain it. Lowering the TTL ahead of the change is what shrinks that window from hours to minutes, in either direction. Respect that, and a safe cutover falls out of it:
That's the whole trick to a zero-downtime DNS cutover: make forward and backward symmetric. If going back is as cheap as going forward, the flip stops being a gamble and becomes an observation.
And I didn't trust the fallback on faith. I ran the failure path for real before the switch — an actual restore drill that validated a ~5-minute recovery objective (RTO) end to end. Not "the docs say we can recover," but a stopwatch on a real restore, confirming the number before anything depended on it. Only once the new environment had proven stable under live traffic did I stand the old one down, retiring the legacy DigitalOcean stack four days early — a call I could make confidently precisely because the rollback net had been real, not theoretical.