Migrating legacy infrastructure to AWS serverless
A big-bang rewrite bets the business on one cutover date. Here is the incremental path: assess, strangle endpoint by endpoint, move the data carefully, and keep every step reversible.
On this page
What I tell people on a call
Do not rewrite. Strangle. Put an API Gateway in front of the legacy system, move endpoints to new services one at a time, and keep both systems live until the new one has earned the traffic. Every step stays reversible, which means a bad week costs you a rollback instead of an outage. The alternative, a full rewrite with a cutover date, concentrates all the risk on a single day and that date will slip.
The rest of this page is how I run that, drawn from a major QSR e-commerce platform in Thailand doing 5M+ orders a month that moved off a monolith endpoint by endpoint, and from estates where getting the existing infrastructure into code turned out to be the long pole.
Assess before you plan anything
Before any target architecture, find out what is actually running. This is consistently less pleasant than expected. On one enterprise estate the audit found fourteen manually provisioned VPCs with overlapping subnets, inconsistent CIDR ranges, and no tagging standard, which meant nothing could be attributed to a team and new environments took two to three weeks to stand up. You cannot plan a migration around a system you cannot describe.
- Inventory every endpoint, job, and integration, including the ones nobody owns.
- Measure traffic shape and latency per endpoint, so you migrate on evidence.
- Map the data stores and who writes to them, including the batch jobs.
- Find the undocumented dependencies now, not during a cutover.
- Note what is compliance-bound, because that constrains where things can move.
Not everything should be serverless
The goal is the right runtime per workload, not a serverless badge. Lambda is the default for spiky, event-driven, short-lived work: API handlers at unpredictable load, S3 and EventBridge triggers, scheduled jobs, fan-out. Per-request billing is exactly right when you are idle much of the time and exactly wrong when you are busy all day.
On the QSR platform, order processing stayed on containers because the workflows were long-lived and latency had to stay tight through promotion-hour spikes, while the event glue and the spiky paths moved to Lambda. That split held P99 under 200ms and cut infrastructure cost by roughly 30% against the old monolith. A migration that forced everything onto Lambda would have achieved neither.
Strangler fig, endpoint by endpoint
Put the gateway in front first, so routing becomes a configuration change rather than a deployment. Then move endpoints individually. Sequence matters: start with low-risk, high-visibility paths to build confidence and prove the pipeline, not with checkout. On the QSR migration we mapped every endpoint, found 12 high-traffic hotspots across 47 endpoints driving 80% of the latency, and moved those early so the wins were visible while the risky paths were still untouched.
Each migrated endpoint is a complete unit of work: new service, tests, observability, alarms, and a route change that can be reverted. If moving an endpoint back requires a deploy and a meeting, the step was too big.
The data cutover
Compute is stateless and reversible. Data is neither. DMS handles database replication and DataSync moves bulk files, and orchestrating those steps through Step Functions puts the retries and timeouts in the open, where a script someone runs by hand leaves them buried. None of that is the difficult part.
The design questions that actually matter: how long do you dual-write, how do you detect divergence between the old store and the new one, and what exactly is the rollback once the new system has accepted writes the old one never saw. Answer those before the first byte moves. A cutover you cannot reverse is not a migration step, it is a launch.
Get the existing estate into code
Migrating into a hand-clicked target just moves the problem. Before or alongside the migration, bring the infrastructure under Terraform or CDK with remote state and locking, so environments are reproducible and drift stops. Budget real time for it: on one platform, importing 180 existing resources into Terraform took three weeks, mostly because resources had dependencies nobody had documented. It was non-negotiable anyway, because without it the drift that caused the original mess simply continues in the new environment.
Parallel run, then cut over
Run both systems against real traffic and compare them before committing. On the QSR platform both ran in parallel for weeks, which let us compare error rates and latency per endpoint, which no staging environment will predict for you. Parallel running is the step teams cut when the schedule slips, and it is the step that catches the problems staging never will.
Have the rollback mechanism in place before you start, not after. Blue-green deploys with a fast rollback turn a bad release into an inconvenience: on that migration one bad service release rolled back in under 30 seconds with no customer impact. That safety net is why we could move fast without gambling.
What changes after the migration
Serverless moves the operational work, it does not remove it. You stop patching servers and start caring about concurrency limits, cold starts on the paths where latency matters, per-function IAM scope, and a cost model that now moves with traffic where it used to sit flat. Set reserved concurrency so one function cannot starve the account. Put a dead-letter queue behind every async consumer and alarm on it. Make consumers idempotent, because events get delivered more than once.
Budget for the observability rebuild too. The dashboards and alarms that described the monolith do not describe a distributed event-driven system, and a migration that lands without them just moves your blind spots.
Mistakes that cost the most
- Rewriting in one cutover instead of strangling endpoint by endpoint.
- Starting with the riskiest path because it is the most interesting one.
- Forcing steady, long-running workloads onto per-millisecond billing.
- Planning the data migration last, when it constrains everything else.
- Migrating into a hand-clicked target environment and inheriting the drift.
- Cutting the parallel run when the schedule slips.
- Landing the migration without rebuilding observability for the new shape.
Next step
Have the same problem on your stack?
Send the architecture, AWS bill concern, deploy pain, or GenAI reliability issue. I'll find the first real bottleneck and propose a small, reversible fix.
FAQ
How do I migrate a legacy monolith to AWS serverless?
Incrementally, behind an API Gateway, using the Strangler Fig pattern. Map every endpoint, pick low-risk and high-visibility paths first, and route them one at a time from the monolith to new services while both systems stay live. On a major QSR e-commerce platform in Thailand we started with the order search API, low risk and high visibility, which built confidence before anyone touched checkout, and ran both systems in parallel for weeks before cutting over. Every step stays reversible, which is the entire point. A big-bang rewrite bets the whole business on a single cutover date that will slip.
How long does a legacy to serverless migration take?
Longer than the rewrite estimate, and that is the honest answer. The parts people forget are the ones that dominate: getting the existing estate into infrastructure as code, migrating data without losing any, and running two systems in parallel long enough to trust the new one. On one platform, importing 180 existing resources into Terraform alone took three weeks, because some of them had undocumented dependencies nobody knew about until the import failed.
Should the whole system become serverless?
No. Serverless fits spiky, event-driven, short-lived work, where per-request billing means you pay nothing at idle. Long-running or steadily busy workloads are usually cheaper and more predictable on right-sized containers. On the QSR platform the order processing stayed on containers precisely because the workflows were long-lived and latency had to hold under promotion-hour spikes, while Lambda took the event glue and the spiky paths. Migrate to the right runtime per workload, not to a label.
What about the database during a migration?
Plan it first, because it constrains everything else. DMS handles the database replication and DataSync the bulk file movement, but the real design question is how long you run dual writes, how you detect divergence between the two stores, and what your rollback looks like once the new system has accepted writes the old one never saw. Cutting over compute is reversible in minutes. Cutting over data is not, unless you designed it to be.
How do we avoid the migration making things worse?
Keep every step reversible and measure both systems against each other while they run in parallel. Compare error rates and latency per endpoint before you move traffic permanently. Have blue-green deploys with a fast rollback in place before the migration starts, not after: on the QSR migration a bad service release rolled back in under 30 seconds with no customer impact, and that safety net is what made an aggressive pace defensible.
Related reading

Rahul Ladumor
Principal Cloud & AI Platform Architect. AWS Professional certified, 5x AWS Community Builder. I work with teams that have real users, real AWS bills, and real production pressure.
About Rahul →