Multi-Region
This guide covers deploying Rack Gateway across multiple AWS regions for geographic distribution, compliance requirements, or disaster recovery.
When to Use Multi-Region
Section titled “When to Use Multi-Region”| Scenario | Recommendation |
|---|---|
| Data residency requirements | Deploy in required regions |
| Disaster recovery | Active-passive with replication |
| Global team distribution | Regional gateways per team location |
| Compliance (GDPR, data sovereignty) | Dedicated EU deployment |
Architecture Patterns
Section titled “Architecture Patterns”Pattern 1: Regional Isolation
Section titled “Pattern 1: Regional Isolation”Each region operates independently with its own gateway and database:
Use when:
- Data must not cross regional boundaries
- Each region manages separate infrastructure
- GDPR or similar compliance requirements
Pattern 2: Active-Passive DR
Section titled “Pattern 2: Active-Passive DR”Primary region handles traffic; secondary is standby:
Use when:
- High availability requirements
- RTO/RPO targets need geographic redundancy
- Cost-effective DR solution needed
Terraform Module Structure
Section titled “Terraform Module Structure”terraform/├── modules/│ └── rack_gateway/│ ├── main.tf│ ├── variables.tf│ └── outputs.tf├── environments/│ ├── us/│ │ ├── main.tf│ │ ├── providers.tf│ │ └── terraform.tfvars│ └── eu/│ ├── main.tf│ ├── providers.tf│ └── terraform.tfvars└── global/ └── dns/ └── main.tfRegional Deployment
Section titled “Regional Deployment”Provider Configuration
Section titled “Provider Configuration”terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }
backend "s3" { bucket = "myorg-terraform-state" key = "rack-gateway/us/terraform.tfstate" region = "us-east-1" dynamodb_table = "terraform-locks" }}
provider "aws" { region = "us-east-1"
default_tags { tags = { Project = "rack-gateway" Environment = "production" Region = "us" } }}provider "aws" { region = "eu-west-1"
default_tags { tags = { Project = "rack-gateway" Environment = "production" Region = "eu" } }}Regional Gateway Module
Section titled “Regional Gateway Module”module "rack_gateway" { source = "../../modules/rack_gateway"
environment = "production" region = "us"
vpc_id = data.aws_vpc.main.id private_subnet_ids = data.aws_subnets.private.ids
db_instance_class = "db.t3.medium" db_password = var.db_password
enable_audit_anchoring = true worm_retention_days = 400
domain = "gateway-us.example.com" rack_alias = "us"
tags = { Region = "us" }}module "rack_gateway" { source = "../../modules/rack_gateway"
environment = "production" region = "eu"
vpc_id = data.aws_vpc.main.id private_subnet_ids = data.aws_subnets.private.ids
db_instance_class = "db.t3.medium" db_password = var.db_password_eu
enable_audit_anchoring = true worm_retention_days = 400
domain = "gateway-eu.example.com" rack_alias = "eu"
tags = { Region = "eu" }}Cross-Region RDS Replication
Section titled “Cross-Region RDS Replication”Read Replica for DR
Section titled “Read Replica for DR”# In DR regionresource "aws_db_instance" "replica" { provider = aws.dr
identifier = "rack-gateway-dr" replicate_source_db = aws_db_instance.primary.arn
instance_class = "db.t3.medium"
# Replica-specific settings storage_encrypted = true kms_key_id = aws_kms_key.database_dr.arn
vpc_security_group_ids = [aws_security_group.rds_dr.id] db_subnet_group_name = aws_db_subnet_group.dr.name
# Backup settings for replica backup_retention_period = 7
tags = { Name = "rack-gateway-dr-replica" Role = "replica" }}Promote Replica on Failover
Section titled “Promote Replica on Failover”# During DR eventaws rds promote-read-replica \ --db-instance-identifier rack-gateway-dr \ --region us-west-2S3 Cross-Region Replication
Section titled “S3 Cross-Region Replication”See S3 WORM Storage for detailed replication configuration.
resource "aws_s3_bucket_replication_configuration" "audit_anchors" { bucket = aws_s3_bucket.audit_anchors.id role = aws_iam_role.replication.arn
rule { id = "replicate-to-dr" status = "Enabled"
destination { bucket = aws_s3_bucket.audit_anchors_dr.arn
replication_time { status = "Enabled" time { minutes = 15 } } } }}DNS and Routing
Section titled “DNS and Routing”Route 53 Health Checks
Section titled “Route 53 Health Checks”Route 53 health checkers come from the internet, so this only works for gateways with a public endpoint. For
gateways on a private network, use a heartbeat monitor (UPTIME_HEARTBEAT_URL) instead.
resource "aws_route53_health_check" "gateway_us" { fqdn = "gateway-us.example.com" port = 443 type = "HTTPS" resource_path = "/api/v1/health" failure_threshold = 3 request_interval = 30
tags = { Name = "rack-gateway-us-health" }}
resource "aws_route53_health_check" "gateway_eu" { fqdn = "gateway-eu.example.com" port = 443 type = "HTTPS" resource_path = "/api/v1/health" failure_threshold = 3 request_interval = 30
tags = { Name = "rack-gateway-eu-health" }}Latency-Based Routing
Section titled “Latency-Based Routing”Route users to nearest healthy region:
resource "aws_route53_record" "gateway_us" { zone_id = aws_route53_zone.main.zone_id name = "gateway.example.com" type = "A"
alias { name = aws_lb.gateway_us.dns_name zone_id = aws_lb.gateway_us.zone_id evaluate_target_health = true }
set_identifier = "us" latency_routing_policy { region = "us-east-1" }
health_check_id = aws_route53_health_check.gateway_us.id}
resource "aws_route53_record" "gateway_eu" { zone_id = aws_route53_zone.main.zone_id name = "gateway.example.com" type = "A"
alias { name = aws_lb.gateway_eu.dns_name zone_id = aws_lb.gateway_eu.zone_id evaluate_target_health = true }
set_identifier = "eu" latency_routing_policy { region = "eu-west-1" }
health_check_id = aws_route53_health_check.gateway_eu.id}Failover Routing
Section titled “Failover Routing”Primary/DR configuration:
resource "aws_route53_record" "gateway_primary" { zone_id = aws_route53_zone.main.zone_id name = "gateway.example.com" type = "A"
alias { name = aws_lb.gateway_primary.dns_name zone_id = aws_lb.gateway_primary.zone_id evaluate_target_health = true }
set_identifier = "primary" failover_routing_policy { type = "PRIMARY" }
health_check_id = aws_route53_health_check.gateway_primary.id}
resource "aws_route53_record" "gateway_dr" { zone_id = aws_route53_zone.main.zone_id name = "gateway.example.com" type = "A"
alias { name = aws_lb.gateway_dr.dns_name zone_id = aws_lb.gateway_dr.zone_id evaluate_target_health = true }
set_identifier = "secondary" failover_routing_policy { type = "SECONDARY" }}CLI Configuration
Section titled “CLI Configuration”Configure the CLI to handle multiple regional gateways:
# Log in to each regional gateway oncerack-gateway login us https://gateway-us.example.comrack-gateway login eu https://gateway-eu.example.comUsage:
# Work with US rackrack-gateway apps --rack us
# Work with EU rackrack-gateway apps --rack eu
# Switch default rackrack-gateway switch euState Synchronization
Section titled “State Synchronization”What Syncs
Section titled “What Syncs”For independently deployed gateways, these items may need synchronization:
| Item | Sync Method | Frequency |
|---|---|---|
| Users | Added on each gateway, or seeded with ADMIN_USERS etc. | As needed |
| RBAC roles | Manual, or the seed lists | As needed |
| API tokens | Region-specific | N/A |
| Audit logs | Each gateway’s own database and anchor chain | Not synced |
User Synchronization
Section titled “User Synchronization”Each gateway has its own users: signing in never creates one. Either:
- Add them on each gateway - an admin adds users and roles on its Users page
- Seed them from configuration - set the same
ADMIN_USERS,DEPLOYER_USERS,OPERATIONS_USERSandVIEWER_USERSon every gateway (for example from one Terraform variable). Each start creates missing users with the list’s role, and leaves existing users alone
Disaster Recovery Runbook
Section titled “Disaster Recovery Runbook”RTO/RPO Targets
Section titled “RTO/RPO Targets”| Metric | Target | Achieved By |
|---|---|---|
| RPO | 15 minutes | S3 replication, RDS replica |
| RTO | 30 minutes | Automated failover |
Failover Procedure
Section titled “Failover Procedure”-
Detect failure
Route 53 health checks automatically detect failure.
-
Verify replication
Terminal window # Check RDS replica lagaws rds describe-db-instances \--db-instance-identifier rack-gateway-dr \--query 'DBInstances[0].ReadReplicaSourceDBInstanceIdentifier' -
Promote RDS replica
Terminal window aws rds promote-read-replica \--db-instance-identifier rack-gateway-dr \--region us-west-2 -
Update gateway configuration
Point DR gateway to promoted database.
-
Verify DR gateway
Terminal window curl https://gateway-dr.example.com/api/v1/health -
DNS failover
Automatic if using Route 53 health checks, or manual:
Terminal window aws route53 change-resource-record-sets ...
Failback Procedure
Section titled “Failback Procedure”After primary is restored:
- Set up new RDS replication (primary → DR)
- Sync any data created during outage
- Verify primary health
- Failback DNS routing
Cost Considerations
Section titled “Cost Considerations”| Component | Single Region | Multi-Region |
|---|---|---|
| RDS | $50-200/mo | $100-400/mo |
| S3 (with CRR) | ~$0/mo | ~$0/mo |
| Route 53 health checks | - | $1.50/mo |
| Data transfer | - | Variable |
Best Practices
Section titled “Best Practices”Regional Isolation
Section titled “Regional Isolation”- Use separate VPCs per region
- Region-specific KMS keys
- Independent IAM roles
Consistency
Section titled “Consistency”- Same Terraform modules across regions
- Consistent naming conventions
- Automated deployment pipelines
Monitoring
Section titled “Monitoring”- CloudWatch dashboards per region
- Cross-region metrics comparison
- Unified alerting
Testing
Section titled “Testing”- Regular DR drills
- Failover automation tests
- Replication lag monitoring
Next Steps
Section titled “Next Steps”- S3 WORM Storage - Audit anchoring with replication
- AWS Infrastructure - Single-region setup
- Production Checklist - Go-live preparation