Writing
Moving a legacy application to AWS without downtime: a sequencing guide
The order of operations that keeps a migration reversible at every step — what to move first, why the database goes last, and the cost lines that surprise teams after the cutover.
6 min read
Most migration plans fail on sequencing rather than on technology. A team moves the database first because it feels foundational, discovers the application cannot tolerate the added latency, and now has a half-migrated system with no way back.
The principle underneath everything here: every step must be reversible on its own. If any single step cannot be undone in minutes, it is too large and needs splitting.
Before anything moves: three things to establish
Know what you actually have
Every cron job, every file written to local disk, every hard-coded IP, every certificate and its expiry. The surprises in a migration are almost always something nobody documented — a nightly script on a machine no one has logged into for two years.
Measure the current system
p50 and p95 latency, requests per second, peak concurrency, database size and growth. You cannot claim an improvement, or notice a regression, without a baseline taken beforehand.
Write the rollback for step one
If you cannot write it, the step is wrong. This is the discipline that keeps a migration from becoming a one-way door.
The order that keeps every step reversible
| # | Step | Reversible by |
|---|---|---|
| 1 | DNS in front of the old system, low TTL | Nothing has moved |
| 2 | Static assets and media to S3 + CloudFront | Point the URLs back |
| 3 | Stateless app on ECS/EKS, still using the old database | Shift traffic weight to zero |
| 4 | Background jobs and crons | Re-enable the old scheduler |
| 5 | Database, with replication and a short cutover | Fail back to the old primary |
| 6 | Decommission — only after a full cycle | Not reversible; hence last |
Step 1: put a traffic layer in front before you need it
Move DNS to Route 53 and lower the TTL to 60 seconds weeks ahead. TTL changes take the old TTL to propagate, so a 24-hour TTL lowered on cutover day is still a 24-hour TTL on cutover day. This is the most common avoidable delay in a migration.
Step 3: the stateless tier, running against the old database
This is the step that de-risks everything after it. The new application runs in AWS and talks to the existing database over a VPN or Direct Connect. It proves the container image, the configuration, the secrets, the logging and the health checks — while the data has not moved and rollback is a weight change.
The application usually needs two changes to be movable at all, and both are worth making regardless:
Nothing may be written to local disk and expected to persist
Uploads to S3, sessions to Redis or a cookie, logs to stdout. A container's filesystem is ephemeral, and this is the assumption legacy applications most often violate.
Configuration comes from the environment, at start-up
Not from a file baked into a build. Otherwise you cannot promote one artefact across environments, and you are rebuilding per environment — which means you did not test what you shipped.
Step 4: background jobs need explicit ownership
Crons are where double-execution happens. During the transition both systems can run the same nightly job, and the second one either fails loudly or — worse — quietly does the work twice.
Move them with an explicit switch, one job at a time, and make each job idempotent before it moves.
Step 5: the database, and the only part that needs a window
Everything else has been reversible. This is the step that needs care.
Replicate continuously into the target, and let it catch up
DMS or native replication. Run it for days, not hours, and watch replication lag until it is consistently near zero.
Verify, do not assume
Row counts per table, checksums on the largest ones, and a sample of business-level invariants — order totals, balances. "Replication reported success" is not verification.
Cut over in a short, planned window
Stop writes, let replication drain to zero, promote the target, point the application at it, resume. Minutes, and rehearsed at least once against production-sized data.
Keep replication running the other way for a day
So failing back remains possible until you are certain.
The cost lines that surprise people
Compute is the line everyone estimates and rarely the one that surprises.
| Line | Why it surprises | What reduces it |
|---|---|---|
| NAT Gateway data | Charged per GB processed; chatty private subnets add up | VPC endpoints for S3 and DynamoDB |
| Cross-AZ transfer | Charged in both directions between availability zones | AZ-aware routing; co-locate chatty pairs |
| CloudWatch Logs ingestion | Priced per GB ingested, not stored | Sample debug logs; shorter retention |
| Load balancer capacity units | Charged on connections and rules, not just traffic | Consolidate listeners; prune rules |
| RDS storage IOPS | Provisioned separately from the instance | Right-size after observing real IOPS |
| Snapshots and backups | Accumulate silently | A lifecycle policy from day one |
Tag everything from the first resource, and enforce it in CI
Owner, environment, service. Retro-tagging a live account is miserable, and without tags you cannot attribute a bill.
Right-size after two weeks of real traffic, not before
Sizing on the old hardware's specification usually over-provisions substantially.
Buy commitments only after that
Savings Plans on an unsettled workload lock in the wrong shape for a year.
What "done" means
Decommissioning is a step, not an afterthought, and it comes after a full business cycle on the new system — a month-end, a payroll run, whatever your peak actually is.
One full cycle passed on the new system
Including the periodic jobs that only run monthly, which are the ones nobody tested.
Failback exercised at least once, deliberately
A rollback path nobody has used is a rollback path you do not have.
Runbooks and dashboards updated to the new architecture
An on-call engineer following a runbook that describes the old system is worse off than one with no runbook.
Then, and only then, turn the old system off
Keep a final snapshot longer than you think you need it.
Start here
Tell us what you are building
Or what is breaking, or what has to go faster. You will get a straight answer from an engineer who would do the work.