Multi-Region Deployment

Deploy across multiple AWS regions for high availability

πŸ’΅ Cost: Multi-region deployments approximately double your infrastructure costs. Consider your RTO/RPO requirements carefully. Pilot Light or Warm Standby can reduce costs while still providing regional redundancy.

Why Multi-Region?

  • Disaster recovery - Survive complete region outages
  • Lower latency - Serve users from nearest region
  • Compliance - Data residency requirements
  • Business continuity - Critical applications must stay online

Deployment Strategies

StrategyRTORPOCostComplexity
Backup & RestoreHoursHoursLowLow
Pilot Light10-30 minMinutesLow-MediumMedium
Warm StandbyMinutesSecondsMedium-HighMedium
Active-ActiveSecondsZeroHigh (2x)High

Start with Pilot Light

For most applications, Pilot Light provides a good balance of cost and recovery time. You can upgrade to Warm Standby or Active-Active as business requirements grow.
Plain Text
ACTIVE-ACTIVE MULTI-REGION ARCHITECTURE

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚   Route 53      β”‚
                    β”‚ (Latency-Based  β”‚
                    β”‚   Routing)      β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚                                 β”‚
            β–Ό                                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ap-southeast-1     β”‚           β”‚    us-east-1        β”‚
β”‚  (Primary)          β”‚           β”‚    (Secondary)      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€           β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚           β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚     ALB       β”‚  β”‚           β”‚  β”‚     ALB       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚           β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚          β”‚          β”‚           β”‚          β”‚          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”  β”‚           β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  ECS Fargate  β”‚  β”‚           β”‚  β”‚  ECS Fargate  β”‚  β”‚
β”‚  β”‚  (2-5 tasks)  β”‚  β”‚           β”‚  β”‚  (2-5 tasks)  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚           β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚          β”‚          β”‚           β”‚          β”‚          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”  β”‚    β—„β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ RDS Primary   │──┼──Replication─▢│ RDS Replica  β”‚  β”‚
β”‚  β”‚ (Multi-AZ)    β”‚  β”‚           β”‚  β”‚ (Read Replica)β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚           β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                     β”‚           β”‚                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β—„β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚      S3       │──┼───CRR─────┼─▢│      S3       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚           β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

CRR = Cross-Region Replication

Provider Configuration

HCL
# providers.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Primary region
provider "aws" {
  region = "ap-southeast-1"
  alias  = "primary"
}

# Secondary region
provider "aws" {
  region = "us-east-1"
  alias  = "secondary"
}

ECR Cross-Region Replication

HCL
# Replicate container images to secondary region
resource "aws_ecr_replication_configuration" "main" {
  provider = aws.primary

  replication_configuration {
    rule {
      destination {
        region      = "us-east-1"
        registry_id = data.aws_caller_identity.current.account_id
      }
    }
  }
}

RDS Cross-Region Read Replica

HCL
# Primary RDS instance (ap-southeast-1)
resource "aws_db_instance" "primary" {
  provider = aws.primary

  identifier     = "myapp-db-primary"
  engine         = "postgres"
  engine_version = "15.4"
  instance_class = "db.t3.medium"

  allocated_storage     = 100
  storage_encrypted     = true
  multi_az              = true
  backup_retention_period = 7

  # Enable automated backups (required for read replica)
  backup_window      = "03:00-04:00"
  maintenance_window = "Mon:04:00-Mon:05:00"

  tags = {
    Name = "myapp-db-primary"
  }
}

# Cross-region read replica (us-east-1)
resource "aws_db_instance" "replica" {
  provider = aws.secondary

  identifier     = "myapp-db-replica"
  instance_class = "db.t3.medium"

  # Point to primary instance
  replicate_source_db = aws_db_instance.primary.arn

  # Replica-specific settings
  backup_retention_period = 7
  storage_encrypted       = true
  kms_key_id              = aws_kms_key.secondary.arn

  # Can be promoted to standalone in DR scenario
  tags = {
    Name = "myapp-db-replica"
  }
}

S3 Cross-Region Replication

HCL
# Primary bucket
resource "aws_s3_bucket" "primary" {
  provider = aws.primary
  bucket   = "myapp-data-ap-southeast-1"
}

resource "aws_s3_bucket_versioning" "primary" {
  provider = aws.primary
  bucket   = aws_s3_bucket.primary.id
  versioning_configuration {
    status = "Enabled"
  }
}

# Secondary bucket
resource "aws_s3_bucket" "secondary" {
  provider = aws.secondary
  bucket   = "myapp-data-us-east-1"
}

resource "aws_s3_bucket_versioning" "secondary" {
  provider = aws.secondary
  bucket   = aws_s3_bucket.secondary.id
  versioning_configuration {
    status = "Enabled"
  }
}

# Replication configuration
resource "aws_s3_bucket_replication_configuration" "primary_to_secondary" {
  provider = aws.primary
  bucket   = aws_s3_bucket.primary.id
  role     = aws_iam_role.replication.arn

  rule {
    id     = "replicate-all"
    status = "Enabled"

    destination {
      bucket        = aws_s3_bucket.secondary.arn
      storage_class = "STANDARD"
    }
  }
}

Latency-Based Routing

Route users to the closest region:

HCL
# Primary region record
resource "aws_route53_record" "primary" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.example.com"
  type    = "A"

  alias {
    name                   = aws_lb.primary.dns_name
    zone_id                = aws_lb.primary.zone_id
    evaluate_target_health = true
  }

  latency_routing_policy {
    region = "ap-southeast-1"
  }

  set_identifier = "primary"
}

# Secondary region record
resource "aws_route53_record" "secondary" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.example.com"
  type    = "A"

  alias {
    name                   = aws_lb.secondary.dns_name
    zone_id                = aws_lb.secondary.zone_id
    evaluate_target_health = true
  }

  latency_routing_policy {
    region = "us-east-1"
  }

  set_identifier = "secondary"
}

Failover Routing

Automatic failover when primary is unhealthy:

HCL
# Health check for primary
resource "aws_route53_health_check" "primary" {
  fqdn              = aws_lb.primary.dns_name
  port              = 443
  type              = "HTTPS"
  resource_path     = "/api/health"
  failure_threshold = 3
  request_interval  = 30

  tags = {
    Name = "primary-health-check"
  }
}

# Primary record (with health check)
resource "aws_route53_record" "primary_failover" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.example.com"
  type    = "A"

  alias {
    name                   = aws_lb.primary.dns_name
    zone_id                = aws_lb.primary.zone_id
    evaluate_target_health = true
  }

  failover_routing_policy {
    type = "PRIMARY"
  }

  set_identifier  = "primary"
  health_check_id = aws_route53_health_check.primary.id
}

# Secondary record (failover target)
resource "aws_route53_record" "secondary_failover" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.example.com"
  type    = "A"

  alias {
    name                   = aws_lb.secondary.dns_name
    zone_id                = aws_lb.secondary.zone_id
    evaluate_target_health = true
  }

  failover_routing_policy {
    type = "SECONDARY"
  }

  set_identifier = "secondary"
}

Recommended Region Pairs

PrimarySecondaryUse Case
ap-southeast-1 (Singapore)ap-northeast-1 (Tokyo)Asia-Pacific focus
ap-southeast-1 (Singapore)us-east-1 (N. Virginia)Global, cost-optimized
us-east-1 (N. Virginia)us-west-2 (Oregon)US-focused
eu-west-1 (Ireland)eu-central-1 (Frankfurt)Europe-focused, GDPR

Your Configuration

This project uses:
  • Primary: ap-southeast-1 (Singapore)
  • Secondary: us-east-1 (N. Virginia)

AWS Deployment Guide β€” Built with Next.js