Skip to content

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/.

  • 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.
┌──────────────────┐
│ 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.

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
  • 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.

From the repository root:

Terminal window
# Build the image
docker 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_URL
docker tag oidc-exchange:latest $ECR_URL:latest
docker push $ECR_URL:latest
Terminal window
cd examples/ecs-fargate/infra
cp terraform.tfvars.example terraform.tfvars

Edit 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"
Terminal window
terraform init
terraform plan
terraform apply

Terraform 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.

Terminal window
ALB_URL=$(terraform output -raw alb_url)
curl $ALB_URL/health
curl $ALB_URL/.well-known/openid-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.

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.

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.

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 to oidc-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.

  • Fargate Spot: Add capacity_provider_strategy with FARGATE_SPOT in the ECS service for up to 70% savings.
  • ElastiCache: The example uses a single cache.t4g.micro node. 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/arm64 and set runtime_platform in the task definition for ~20% lower Fargate cost.
Terminal window
terraform destroy

This removes all AWS resources created by the example.