Back to blog
Cloud MigrationAWSContainersBlue-Green

How to Execute a Zero-Downtime Cloud Migration

4 min read

"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:

  1. **Day 1**: 5% of traffic to cloud, 95% on-prem. Monitor error rates, latency, and data consistency
  2. **Day 2**: If metrics are clean, bump to 25%
  3. **Day 3**: 50/50 split
  4. **Day 4**: 90% cloud, 10% on-prem
  5. **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