ECS Fargate
Run oidc-exchange on Amazon Elastic Container Service (ECS) with Fargate for automatic scaling. An Application Load Balancer (ALB) distributes requests. DynamoDB stores users, and ElastiCache Valkey stores refresh token sessions.
Infrastructure is managed with Terraform. A runnable example is in examples/ecs-fargate/.
When to use this
Section titled “When to use this”- You need high availability with automatic scaling.
- You want to run containers without managing servers or Kubernetes.
- You want Valkey for session lookups and DynamoDB for persistent user records.
- You already use AWS.
Architecture
Section titled “Architecture” ┌──────────────────┐ │ ALB │ │ (TLS termination)│ └────────┬─────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐ │ Fargate │ │ Fargate │ │ Fargate │ │ Task 1 │ │ Task 2 │ │ Task N │ └──┬─────┬───┘ └──┬─────┬───┘ └──┬─────┬───┘ │ │ │ │ │ │ ┌──────┴──┐ │ ┌──────┴──┐ │ ┌──────┴──┐ │ │DynamoDB │ │ │DynamoDB │ │ │DynamoDB │ │ │ (users) │ │ │ (users) │ │ │ (users) │ │ └─────────┘ │ └─────────┘ │ └─────────┘ │ ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐ │ElastiCache │ │ElastiCache │ │ElastiCache │ │ Valkey │ │ Valkey │ │ Valkey │ │(sessions) │ │(sessions) │ │(sessions) │ └────────────┘ └────────────┘ └────────────┘All Fargate tasks share the same DynamoDB table and ElastiCache cluster.
Infrastructure
Section titled “Infrastructure”| Resource | Purpose | Managed by |
|---|---|---|
| Virtual private cloud (VPC) | Network isolation across two availability zones, with public and private subnets | Terraform |
| ALB | Handle Transport Layer Security (TLS), check service health, and distribute traffic | Terraform |
| ECS Cluster + Service | Fargate task orchestration, auto-scaling (2-20 tasks) | Terraform |
| DynamoDB | User storage (on-demand billing, single-table design) | Terraform |
| ElastiCache Valkey | Session storage (single-node, in-VPC) | Terraform |
| AWS Key Management Service (KMS) | Sign JSON Web Tokens (JWTs) with ECC_NIST_P256 | Terraform |
| Amazon Simple Queue Service (SQS) | Queue audit events | Terraform |
| Amazon Elastic Container Registry (ECR) | Store container images | Terraform |
| Secrets Manager | Store OpenID Connect (OIDC) client secrets | Terraform |
Prerequisites
Section titled “Prerequisites”- Terraform 1.5+.
- The AWS command-line interface (CLI), configured with credentials.
- Docker (to build and push the container image).
- A Google OAuth client ID and secret (or another OIDC provider).
- An optional AWS Certificate Manager (ACM) certificate and its Amazon Resource Name (ARN) for HTTPS.
Deployment
Section titled “Deployment”1. Build and push the container image
Section titled “1. Build and push the container image”From the repository root:
# Build the imagedocker build -t oidc-exchange -f examples/ecs-fargate/Dockerfile .
# The Terraform config creates an ECR repository. If deploying for the first time,# run terraform apply first (step 3), then push:ECR_URL=$(terraform -chdir=examples/ecs-fargate/infra output -raw ecr_repository_url)aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_URLdocker tag oidc-exchange:latest $ECR_URL:latestdocker push $ECR_URL:latest2. Configure Terraform variables
Section titled “2. Configure Terraform variables”cd examples/ecs-fargate/infracp terraform.tfvars.example terraform.tfvarsEdit terraform.tfvars:
aws_region = "us-east-1"container_image = "123456789012.dkr.ecr.us-east-1.amazonaws.com/oidc-exchange:latest"google_client_id = "your-google-client-id"google_client_secret = "your-google-client-secret"issuer_url = "https://auth.example.com"# certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/xxx"3. Deploy
Section titled “3. Deploy”terraform initterraform planterraform applyTerraform creates the resources listed in the infrastructure table. It also creates network subnets, a network address translation (NAT) gateway, access roles, security groups, and scaling policies.
Because the ALB is the client-facing hop, configure server.trusted_proxies with the task-visible ALB/VPC CIDRs and set trusted_proxy_hops = 1. When rate limiting is enabled without trusted proxies, startup warns that all clients behind the ALB would otherwise share one proxy-address budget.
4. Verify
Section titled “4. Verify”ALB_URL=$(terraform output -raw alb_url)curl $ALB_URL/healthcurl $ALB_URL/.well-known/openid-configurationConfiguration
Section titled “Configuration”The TOML configuration at examples/ecs-fargate/config/fargate.toml uses environment variable placeholders. Keep /token and /revoke protected by the configured public budgets; /health, discovery, and JWKS remain available for health checks and key discovery and are not charged to authentication-failure budgets. Terraform injects the actual values via the ECS task definition:
[repository]adapter = "dynamodb"
[repository.dynamodb]table_name = "${DYNAMODB_TABLE_NAME}"
[session_repository]adapter = "valkey"
[session_repository.valkey]url = "${VALKEY_URL}"key_prefix = "oidc:"When [session_repository] is present, sessions use Valkey while users stay in DynamoDB. Remove the [session_repository] section to store everything in DynamoDB.
Auto-scaling
Section titled “Auto-scaling”Terraform adjusts the number of tasks to keep average processor (CPU) usage near a target:
- Target: 60% average CPU utilization.
- Min tasks: 2 (configurable via
desired_count). - Max tasks: 20 (configurable via
max_count). - Scale-out cooldown: 60 seconds.
- Scale-in cooldown: 300 seconds.
Modify variables.tf defaults or override in terraform.tfvars to tune scaling behavior.
Security groups
Section titled “Security groups”| Resource | Inbound | Outbound |
|---|---|---|
ALB (sg-alb) |
80,443/tcp from 0.0.0.0/0 | 8080/tcp to sg-task |
Fargate tasks (sg-task) |
8080/tcp from sg-alb |
443/tcp to 0.0.0.0/0 (OIDC providers, AWS APIs) |
Fargate tasks (sg-task) |
(none) | 6379/tcp to sg-valkey |
ElastiCache (sg-valkey) |
6379/tcp from sg-task |
(none) |
DynamoDB, KMS, SQS, and Secrets Manager are accessed via AWS service endpoints (HTTPS over port 443). For private networking, add VPC endpoints for these services.
IAM roles
Section titled “IAM roles”Terraform creates two Identity and Access Management (IAM) roles:
Task execution role, used by ECS to pull images, read secrets, and write logs:
ecr:GetAuthorizationToken,ecr:BatchGetImage,ecr:GetDownloadUrlForLayer.secretsmanager:GetSecretValue(scoped tooidc-exchange/*).logs:CreateLogStream,logs:PutLogEvents.
Task role, used by the running container to access AWS services:
- DynamoDB:
GetItem,PutItem,UpdateItem,DeleteItem,Query,BatchWriteItem. - KMS:
Sign,GetPublicKey. - SQS:
SendMessage.
ElastiCache Valkey access is controlled via security groups (VPC networking), not IAM.
Cost optimization
Section titled “Cost optimization”- Fargate Spot: Add
capacity_provider_strategywithFARGATE_SPOTin the ECS service for up to 70% savings. - ElastiCache: The example uses a single
cache.t4g.micronode. Use a replication group for high availability or ElastiCache Serverless for automatic scaling. - DynamoDB on-demand: You do not need to plan capacity. For steady traffic, consider provisioned capacity.
- ARM64: Build for
linux/arm64and setruntime_platformin the task definition for ~20% lower Fargate cost.
Cleanup
Section titled “Cleanup”terraform destroyThis removes all AWS resources created by the example.