Cloud Migration Patterns

Plan and execute cloud migrations using proven patterns. Covers the 7 Rs of migration, lift-and-shift vs re-architecture, database migration strategies, cutover planning, and post-migration optimization.

Cloud migration patterns are the structured approaches engineers use to move workloads from on-premises infrastructure or legacy cloud environments into modern, scalable, resilient cloud platforms. These patterns define how applications, data, and services are transformed, decomposed, and reassembled across cloud boundaries—critical during organizational transformation, modernization initiatives, or when adopting cloud-native principles. Each pattern addresses a distinct challenge in the journey from monolith to distributed systems, and choosing the right one depends on existing assets, team maturity, and desired outcomes.

Lift and Shift (Rehosting)

Move existing applications with minimal modification, preserving architecture and dependencies. Ideal for legacy systems with high development cost to refactor.

Key failure modes:

Common confusions:

Replatform (Enhanced Rehosting)

Apply cloud-specific enhancements with minimal code changes—improving performance and cost without full refactoring.

Example configuration:


Resources:
  WebAppInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-12345678
      InstanceType: t3.medium
      SecurityGroups:
        - Ref: WebSecurityGroup
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeSize: 50
            VolumeType: gp2
            DeleteOnTermination: true
      UserData:
        Fn::Base64: |
          #!/bin/bash
          yum update -y
          yum install -y nginx
          aws s3 cp s3://app-assets/config.json /etc/app/
          systemctl enable nginx
          systemctl start nginx

Sharp edges:

Refactor (Rearchitecture)

Break monoliths into smaller, loosely coupled services, often with containerization and microservices principles.

Example deployment:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 4
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
        - name: user-service
          image: registry.example.com/user-service:1.2.0
          ports:
            - containerPort: 8080
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: connection-string
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"

Failure modes:

Rebuild (New Platform, New Code)

Build entirely new applications tailored to the cloud, using modern frameworks and cloud-native patterns.

Example Terraform module:

# modules/order-processing/main.tf
resource "aws_lambda_function" "process_order" {
  filename         = "lambda.zip"
  function_name    = "process-order"
  role             = aws_iam_role.lambda_exec.arn
  handler          = "index.handler"
  runtime          = "python3.9"
  timeout          = 30
  memory_size      = 512
  publish          = true

  environment {
    variables = {
      TABLE_NAME = aws_dynamodb_table.orders.name
    }
  }

  tags = {
    Environment = var.environment
    Project     = "OrderService"
  }
}

resource "aws_event_source_mapping" "sqs_to_lambda" {
  event_source_arn = aws_sqs_queue.orders.arn
  function_name    = aws_lambda_function.process_order.arn
  batch_size       = 10
  maximum_batching_window_in_seconds = 300
}

Common pitfalls:

Retire (Decommissioning)

Remove outdated systems and consolidate workloads after migration.

Configuration example:

# script: retire-old-app.sh
#!/bin/bash
set -euo pipefail

export AWS_PROFILE=prod

# 1. Mark instances for retirement
aws ec2 create-tags \
  --resources $(aws ec2 describe-instances \
    --filters "Name=tag:Application,Values=LegacyApp" \
    --query 'Reservations[].Instances[].InstanceId' \
    --output text) \
  --tags Key=Retire,Value=true

# 2. Start backup to S3
aws s3 sync /var/data/legacy-app s3://backup-legacy-app/ --delete

# 3. Terminate after 30 days
aws ec2 create-ami \
  --instance-id i-1234567890abcdef0 \
  --name "LegacyApp-Backup-$(date +%Y-%m-%d)" \
  --description "Backup for retirement" \
  --no-include-volume-attachments

# 4. Schedule termination
aws ec2 create-scheduled-action \
  --scheduled-action-name "Retire-LegacyApp" \
  --schedule "cron(0 0 1 * *)" \
  --recurrence "monthly" \
  --target-type "instance" \
  --target-resource-id "i-1234567890abcdef0" \
  --action-type "terminate" \
  --start-time "2023-10-01T00:00:00Z" \
  --end-time "2024-12-31T23:59:59Z"

Silent failures:

Data-Driven Migration

Move data across environments with consistency, integrity, and minimal downtime.

Example data migration:

# Sync data with incremental updates
rsync -avz --partial --delete \
  --include='*/' \
  --include='*.csv' \
  --include='*.json' \
  --exclude='*' \
  /data/ \
  user@cloud-host:/data/

# Monitor with log-based replication
# In MySQL:
SHOW SLAVE STATUS\G
-- Output includes:
-- Last_IO_Error: Got error 1045 from server: Access denied for user 'repl'@'%' (using password: YES)
-- Last_SQL_Error: Failed to execute transaction on table users
-- Seconds_Behind_Master: 32

# Verify data consistency
pg_dump -h old-db.example.com -U app_user -d legacy_app | \
  pg_isready -h new-db.example.com -U app_user -d new_app

Failure modes:

Incremental Migration (Phased Rollout)

Apply changes in small, predictable steps, minimizing risk and downtime.

Example:

# api-gateway.yaml
resources:
  - type: AWS::ApiGateway::RestApi
    properties:
      name: user-api
      description: "User service API"
      endpointConfiguration:
        types: ["REGIONAL"]
      binaryMediaTypes:
        - "application/json"
        - "image/png"
    # Route /users to both v1 and v2
    - type: AWS::ApiGateway::Deployment
      properties:
        apiId: !Ref UserApi
        stageName: v1
    - type: AWS::ApiGateway::Stage
      properties:
        apiId: !Ref UserApi
        stageName: v2
        description: "Feature flag: new user creation"
        variables:
          feature.new-user.create: "true"
        accessLogSettings:
          destinationArn: arn:aws:logs:us-east-1:123456789012:log-group:/api/user-api
          format: "$context.requestId $context.status $context.path $context.authorizer.claims.email"

Sharp edges:

Hybrid Migration

Maintain on-premises and cloud systems in parallel, with seamless integration.

Common configuration:

// dms-task-configuration.json
{
  "FullLoadSettings": {
    "MaxFullLoadDistinctValueCount": 100000,
    "TargetTablePrepMode": "TRUNCATE"
  },
  "TaskSettings": {
    "Logging": {
      "LogGroupName": "/aws/dms/replication-task",
      "LogFileSize": 1024,
      "LogRetentionInDays": 30
    },
    "ParallelLoad": {
      "LoadParallelism": 4,
      "LoadThreshold": 10000
    }
  }
}

Failure modes:

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.