ADR-002: Cross-Region Bedrock Architecture for Image Generation
Status
Accepted
Date
2025-12-10
Context
Momentum’s AI-powered course generation requires thumbnail images generated using AI models. We evaluated several options:
- Amazon Titan Image Generator - Available in us-east-1 (our primary region), but produces lower-quality results
- Stability AI SDXL - Available in us-east-1, but produces inconsistent quality for educational thumbnails
- Stability AI SD 3.5 Large - Produces the highest-quality images, but is only available in us-west-2
Our primary infrastructure is deployed in us-east-1 (API Gateway, Lambda, Aurora, S3, Cognito). The best image generation model (Stable Diffusion 3.5 Large) is only available in us-west-2.
Key Constraints
- SD 3.5 Large is only available in specific regions (us-west-2, eu-west-1, ap-northeast-1)
- All other infrastructure (database, API, authentication) is in us-east-1
- Bedrock model invocation is synchronous - no async option available
- Thumbnail generation is part of the course generation workflow (Step Functions)
Decision
We adopted a cross-region Bedrock invocation pattern where:
- Lambda functions remain in us-east-1 with the rest of the infrastructure
- Bedrock calls for SD 3.5 Large are made cross-region to us-west-2
- Generated images are uploaded to S3 in us-east-1 (same region as other assets)
Implementation Details
Lambda Configuration
The generate-thumbnail Lambda:
- Deployed in us-east-1 (same as other Lambdas)
- Invokes Bedrock SD 3.5 Large in us-west-2
- Does NOT use VPC (only needs public AWS endpoints)
- Uploads generated images to S3 in us-east-1
// Lambda creates a Bedrock client targeting us-west-2
const BEDROCK_IMAGE_REGION = process.env.BEDROCK_IMAGE_REGION || 'us-west-2';
const bedrockClient = new BedrockRuntimeClient({ region: BEDROCK_IMAGE_REGION });
IAM Policy Configuration
The Lambda execution role has two separate Bedrock policy statements:
# Primary region models (us-east-1)
{
Sid = "AllowBedrockModelInvocation"
Effect = "Allow"
Action = ["bedrock:InvokeModel"]
Resource = [
"arn:aws:bedrock:${var.aws_region}::foundation-model/anthropic.claude-*",
"arn:aws:bedrock:${var.aws_region}::foundation-model/amazon.titan-image-generator-v2:0"
]
}
# Cross-region model (us-west-2 for SD 3.5 Large)
{
Sid = "AllowBedrockSD35LargeInvocation"
Effect = "Allow"
Action = ["bedrock:InvokeModel"]
Resource = [
"arn:aws:bedrock:us-west-2::foundation-model/stability.sd3-5-large-v1:0"
]
}
Environment Variables
environment {
variables = {
BEDROCK_IMAGE_MODEL_ID = "stability.sd3-5-large-v1:0"
BEDROCK_IMAGE_REGION = "us-west-2" # Cross-region target
}
}
Why No VPC for Thumbnail Lambda
The thumbnail generation Lambda intentionally runs without VPC configuration:
- No database access needed - receives course metadata from Step Functions state
- Only uses public AWS endpoints - Bedrock and S3 are accessible via public internet
- Faster cold starts - no ENI attachment delay (saves 5-10 seconds)
- No NAT gateway costs - no outbound traffic through VPC
Consequences
Positive
- Highest quality thumbnails - SD 3.5 Large produces significantly better results than alternatives
- No infrastructure migration - Core infrastructure remains in us-east-1
- Minimal latency impact - Cross-region Bedrock call adds ~50-100ms
- Cost-effective - No NAT gateway, no cross-region data transfer for final assets
- Simple architecture - Single Lambda invokes multiple regions as needed
- Future-proof - Can easily add other region-specific models
Negative
- Region availability dependency - If us-west-2 Bedrock has issues, thumbnail generation fails
- Cross-region IAM complexity - Need separate policy statements per region
- Monitoring complexity - Need to monitor Bedrock metrics in both regions
- Pricing variation - Model pricing may differ between regions
Risks and Mitigations
| Risk | Mitigation |
|---|---|
| us-west-2 Bedrock outage | Workflow continues without thumbnail; can regenerate later |
| Model deprecation | Environment variable allows quick model switch |
| Increased latency | Acceptable for batch operations; not user-facing |
| Cost unpredictability | SD 3.5 Large pricing is predictable per-image |
Alternatives Considered
Alternative 1: Multi-Region Lambda Deployment
Deploy a separate Lambda in us-west-2 for image generation.
Rejected because:
- Increased infrastructure complexity
- Need for cross-region S3 transfer
- Additional deployment pipeline
- Higher maintenance burden
Alternative 2: Use Lower-Quality Model in us-east-1
Use Amazon Titan or SDXL in us-east-1 instead.
Rejected because:
- Significantly lower image quality
- Thumbnails are customer-facing assets
- Quality directly impacts course appeal
Alternative 3: Migrate All Infrastructure to us-west-2
Move everything to a region with SD 3.5 Large.
Rejected because:
- Major infrastructure change
- Potential latency impact for users
- us-east-1 has better service availability overall
- Disruption to existing services
Alternative 4: Use External Image Generation API
Use Stability AI’s direct API instead of Bedrock.
Rejected because:
- Additional vendor relationship
- Separate billing and authentication
- Loss of Bedrock’s unified interface
- Potential compliance concerns
References
- Bedrock SD 3.5 Large Documentation
- Bedrock Regional Availability
- ADR-001: Modularity Refactoring
- Infrastructure:
infrastructure/terraform/ai-generation.tf - Handler:
backend/functions/ai-generation/src/handlers/generate-thumbnail.ts