Skip to main content

ECS Fargate

After Phase 7 — EC2 → ECS migration, all backend and AI compute runs on AWS ECS Fargate. This page is the canonical inventory of cluster, services, networking, and operational controls.

Cluster

PropertyValue
Cluster nameaiqlick
Regioneu-north-1
Capacity providersFARGATE (default), FARGATE_SPOT (allowed for non-prod)
Container InsightsEnabled
ECS ExecEnabled on every service (ssmmessages:* granted via task role)

A single cluster hosts both dev and prod for both services. Per-env isolation is at the IAM, security-group, and SQS-queue level — not at the ECS-cluster level.

Services

ServiceTask definition familyDesired (min/max)Public ALBInternal ALBCloud Map
backend-devaiqlick-backend-dev1 / 1 / 4tg-backend-dev (host: api-dev.aiqlick.com)backend-dev.aiqlick.local
backend-prodaiqlick-backend-prod2 / 2 / 8tg-backend-prod (host: api.aiqlick.com)backend-prod.aiqlick.local
bg-tasks-devaiqlick-bg-tasks-dev1 / 1 / 4tg-bg-dev (host: bg-dev.aiqlick.local)bg-dev.aiqlick.local
bg-tasks-prodaiqlick-bg-tasks-prod2 / 2 / 8tg-bg-prod (host: bg-prod.aiqlick.local)bg-prod.aiqlick.local

All services run in the private subnets (172.31.96.0/20, 172.31.112.0/20, 172.31.128.0/20 across the 3 eu-north-1 AZs). assignPublicIp=DISABLED — egress goes through VPC endpoints (in-region) or the NAT gateway (cross-region eu-west-1).

Task sizing

Task familyCPUMemory
aiqlick-backend-dev0.5 vCPU1 GB
aiqlick-backend-prod1 vCPU2 GB
aiqlick-bg-tasks-dev0.5 vCPU1 GB
aiqlick-bg-tasks-prod1 vCPU2 GB
aiqlick-prisma-migrate0.5 vCPU1 GB

Deployment configuration

Every service uses the deployment circuit breaker with auto-rollback:

deploymentCircuitBreaker = { enable: true, rollback: true }
maximumPercent = 200
minimumHealthyPercent = 100
healthCheckGracePeriod = 120s

If the new task revision fails ALB health checks repeatedly, ECS rolls back to the previous successful deployment automatically. This replaces the manual :previous tag rollback the EC2 BG workflow used.

Autoscaling

Application Auto Scaling target tracking on ECSServiceAverageCPUUtilization = 70%.

ServiceMinMax
backend-dev14
backend-prod28
bg-tasks-dev14
bg-tasks-prod28

Scale-out cooldown 60s, scale-in cooldown 300s — bias toward keeping capacity around longer than strictly necessary, since scale-out is fast (~30s ENI provisioning) and scale-in churn is the bigger UX risk (stops-mid-request, dropped WS connections).

ALB topology

ALBSchemeListenerCertSticky
aiqlick-public-albinternet-facingHTTPS:443 + HTTP:80 (plain forward, redirect deferred)ACM api.aiqlick.com SAN api-dev.aiqlick.com (eu-north-1)Yes (lb_cookie 1h)
aiqlick-internal-albinternalHTTP:80None (private VPC, internal-service JWT auth)No

Sticky sessions on the public ALB are required for WebSocket subscriptions and SSE — without them a client reconnect under load lands on a different task and loses the in-process subscription state. The internal ALB is request/response so stickiness is unnecessary.

IAM

RoleTrust principalUsed by
aiqlick-ecs-execution-role-{dev,prod}ecs-tasks.amazonaws.comFargate runtime — pull image, write CloudWatch logs, read Secrets Manager bundle
aiqlick-ecs-backend-task-role-{dev,prod}ecs-tasks.amazonaws.comBackend app — S3 (uploads bucket), SQS (mail / notifications / inbound), SNS (pubsub fanout), EventBridge PutEvents, AppConfig read, X-Ray
aiqlick-ecs-bg-task-role-{dev,prod}ecs-tasks.amazonaws.comBG app — Bedrock / Rekognition / Transcribe / Polly, S3, SQS (inbound + agent-docs + bg-events), SNS publish, AppConfig read, X-Ray
aiqlick-scheduler-rolescheduler.amazonaws.comEventBridge Scheduler — invokes aiqlick-cron-destination API destination for cron POSTs
github-actions-roleOIDC (GitHub)CI deploys — ECR push, ECS RegisterTaskDefinition / UpdateService, ECS RunTask for prisma-migrate

Each task role's full policy is committed alongside the role name at aiqlick-backend/infrastructure/ecs/iam/ and background-tasks/infrastructure/ecs/iam/.

Logging

CloudWatch Log groups (30-day retention):

  • /ecs/aiqlick-backend-{dev,prod}
  • /ecs/aiqlick-bg-tasks-{dev,prod}
  • /ecs/aiqlick-prisma-migrate

Streams are named task/{containerName}/{taskId} so per-task isolation is straightforward to filter on.

VPC endpoints

To keep Fargate tasks off the public internet for AWS service traffic, nine interface endpoints + one S3 gateway endpoint are attached to the private subnets:

  • ecr.api, ecr.dkr — image pull
  • logs — awslogs driver
  • secretsmanager — task-def secret resolution
  • sqs, sns, events — async messaging plane
  • appconfig, appconfigdata — feature flag polling
  • bedrock-runtime — BG LLM calls (in-region)
  • s3 (Gateway) — uploads bucket access (free)

Cross-region calls (Rekognition / Transcribe / Polly in eu-west-1) go via the NAT gateway in AZ-A — there's no eu-north-1 endpoint available for those services.

Cost (per month, eu-north-1 list price)

Component$/month
4× Fargate avg 0.75 vCPU / 1.5 GB~80
Public ALB~22
Internal ALB~22
NAT gateway~32
9× Interface VPC endpoints~50
Total~206

Common operations

# Health snapshot of all services
aws ecs describe-services --profile Administrator-842697652860 --region eu-north-1 \
--cluster aiqlick \
--services backend-prod backend-dev bg-tasks-prod bg-tasks-dev \
--query 'services[*].{name:serviceName,running:runningCount,desired:desiredCount,rollout:deployments[0].rolloutState}' \
--output table

# Open a shell in a running task (replaces SSH)
TASK=$(aws ecs list-tasks --profile Administrator-842697652860 --region eu-north-1 \
--cluster aiqlick --service-name backend-dev --query 'taskArns[0]' --output text)
aws ecs execute-command --profile Administrator-842697652860 --region eu-north-1 \
--cluster aiqlick --task "$TASK" --container aiqlick-backend \
--interactive --command '/bin/sh'

# Tail logs
aws logs tail --profile Administrator-842697652860 --region eu-north-1 \
--follow --format short /ecs/aiqlick-backend-prod

# Force redeploy (no code change)
aws ecs update-service --profile Administrator-842697652860 --region eu-north-1 \
--cluster aiqlick --service backend-prod --force-new-deployment

# Rollback to a previous task definition revision
aws ecs update-service --profile Administrator-842697652860 --region eu-north-1 \
--cluster aiqlick --service backend-prod \
--task-definition aiqlick-backend-prod:42