How to Execute a Zero-Downtime Cloud Migration
"We need to move to the cloud, but we can't afford any downtime." We hear this from almost every enterprise client. The good news: zero-downtime migration isn't a fantasy — it's a well-understood engineering discipline.
We recently migrated 200+ services from on-prem to AWS for an enterprise SaaS platform without a single second of user-facing downtime. Here's the playbook.
Phase 1: Assessment
Before writing a single line of Terraform, you need a complete map of what you're migrating. This phase typically takes 1-2 weeks and covers:
Service inventory: Document every service, its dependencies, data stores, and communication patterns. You can't migrate what you don't understand.
Traffic analysis: Understand peak hours, request volumes, and latency requirements. This drives your capacity planning in the cloud.
Data classification: Identify which data is stateful, which needs real-time sync, and which can tolerate eventual consistency during migration. Databases are always the hardest part.
Risk ranking: Rank services by migration complexity. Start with stateless services (easy wins), then tackle stateful services, and save databases for last.
Phase 2: Containerization
Most legacy services aren't containerized. Before they can run in the cloud on ECS or Kubernetes, they need to be Docker-ized.
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]Key principles:
- **Multi-stage builds** keep images small
- **Health checks** in every container so orchestrators know when a service is ready
- **12-factor app compliance**: config via environment variables, logs to stdout, no local state
Run the containerized version alongside the existing deployment in your on-prem environment first. This validates the container works before you add the complexity of a new infrastructure.
Phase 3: Blue-Green Infrastructure
Build the entire target infrastructure in the cloud before migrating a single request. We use Terraform to provision:
- VPC with public/private subnets across 3 AZs
- ECS clusters (or EKS, depending on complexity)
- Load balancers with health checks
- RDS instances with read replicas
- ElastiCache for session storage
- CloudWatch for monitoring
The "blue" environment is your current on-prem setup. The "green" environment is the new cloud setup. Both run simultaneously.
┌─────────────┐ ┌──────────────┐
│ DNS / │────▶│ On-Prem │ ◀── Blue (current)
│ Route 53 │ │ Services │
│ │────▶│ AWS Cloud │ ◀── Green (new)
└─────────────┘ └──────────────┘Phase 4: Data Synchronization
Databases need special handling. You can't just dump and restore — that requires downtime. Instead:
AWS DMS (Database Migration Service) for continuous replication from on-prem to RDS. It handles the initial full load and then streams changes in real-time.
Dual-write pattern for services that write to both databases during the transition. This adds complexity but ensures zero data loss.
Verification: Run consistency checks between the source and target databases continuously. Any drift gets flagged immediately.
Phase 5: Traffic Shifting
This is where zero-downtime actually happens. Use weighted DNS routing (Route 53) to gradually shift traffic:
- **Day 1**: 5% of traffic to cloud, 95% on-prem. Monitor error rates, latency, and data consistency
- **Day 2**: If metrics are clean, bump to 25%
- **Day 3**: 50/50 split
- **Day 4**: 90% cloud, 10% on-prem
- **Day 5**: 100% cloud, on-prem in standby
aws route53 change-resource-record-sets \
--hosted-zone-id Z123456 \
--change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "api.example.com",
"Type": "A",
"SetIdentifier": "cloud",
"Weight": 50,
"AliasTarget": {
"HostedZoneId": "Z987654",
"DNSName": "alb-cloud.us-east-1.elb.amazonaws.com",
"EvaluateTargetHealth": true
}
}
}]
}'At every step, you have a rollback button: shift traffic back to on-prem. This is why the blue environment stays running throughout.
Phase 6: Monitoring During Migration
You need real-time visibility into both environments during the shift. Our standard stack:
- **Prometheus + Grafana** dashboards showing both environments side-by-side
- **Custom migration dashboard** tracking: requests per environment, error rate delta, p99 latency comparison, database replication lag
- **Automated alerts** that trigger rollback if error rates exceed thresholds
Set the alert thresholds before you start shifting traffic. "If cloud error rate exceeds on-prem error rate by more than 0.5%, halt and roll back."
The Result
Our last migration following this playbook: 200+ services migrated, zero seconds of downtime, 60% infrastructure cost reduction post-migration. The entire cutover happened while customers were actively using the platform — they had no idea anything changed.
The key takeaway: zero-downtime migration isn't about being clever. It's about being methodical. Build the new environment, validate it, shift traffic gradually, and always keep the rollback button within reach.
Planning a cloud migration? Let's talk — we'll assess your infrastructure and build a migration plan with zero-downtime guarantees.
Need help with your infrastructure?
We build production-grade DevOps pipelines, cloud architectures, and SRE practices.
Start a project