AWS ECS Fargate skill for Codex
Container deploys that roll themselves back. Codex writes Fargate task definitions with valid CPU and memory pairs, digest-pinned images, secrets pulled at start and separate execution and task roles. Services get the deployment circuit breaker, a sensible deregistration delay and SIGTERM-aware stop timeouts, and when tasks fail Codex reads stoppedReason and service events against a table of known causes before touching anything.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- aws-ecs-fargate Installs to ~/.agents/skills/aws-ecs-fargate/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install aws-ecs-fargate The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project aws-ecs-fargate Installs to .agents/skills/aws-ecs-fargate/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/aws-ecs-fargate.zipunzip -o aws-ecs-fargate.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) aws-ecs-fargate PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $aws-ecs-fargate, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Designs and operates Amazon ECS services on AWS Fargate - task definitions with valid CPU/memory pairs, execution role vs task role, secrets from Secrets Manager or SSM, container health checks, ALB target groups, rolling deployments with the circuit breaker, blue/green, Fargate Spot capacity providers, autoscaling and ECS Exec debugging. Use when containerizing a service onto ECS, writing or fixing a task definition, a deployment hangs or keeps rolling back, tasks fail to start or get killed, or tuning ECS cost and scaling.
Use it when
- Deploying a container service or worker to ECS on Fargate
- Writing or reviewing a task definition
- Deployments hang, roll back or tasks keep stopping
- Tuning ECS autoscaling or adding Fargate Spot
Use something else when
- You are running on Kubernetes or EKS: use kubernetes-manifests
- The Dockerfile itself needs work: use dockerfile-best-practices
How to check the result
aws ecs wait services-stable succeeds, the deployment rolloutState is COMPLETED, target group health is green, and a deliberately broken image rolls back on its own.
The mistake it prevents
Reusing the execution role as the task role, which hands the application ECR, logs and every secret the agent can read.
Done well means: a bad image rolls back automatically within minutes, tasks drain connections before they stop, secrets never appear in the task definition, each task role grants only what the app calls, and scaling follows a metric that reflects real load.
Before changing anything
- Read
AGENTS.md, the Dockerfile and the IaC that defines the service (CDK, Terraform, Copilot leftovers, raw JSON). Change the IaC, not the console. - Inspect the live service when credentials and network are available:
aws ecs describe-services --cluster prod --services orders \
--query 'services[0].{desired:desiredCount,running:runningCount,td:taskDefinition,deployments:deployments[].{status:status,rollout:rolloutState,running:runningCount,failed:failedTasks},events:events[:8].message}'
aws ecs describe-task-definition --task-definition orders --query taskDefinition
aws ecs list-tasks --cluster prod --service-name orders --desired-status STOPPED
aws ecs describe-tasks --cluster prod --tasks <arn> --query 'tasks[].{reason:stoppedReason,containers:containers[].{name:name,exit:exitCode,reason:reason}}'
Service events and a stopped task’s stoppedReason explain most failures.
Task definition
{
"family": "orders",
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "512",
"memory": "1024",
"runtimePlatform": { "cpuArchitecture": "ARM64", "operatingSystemFamily": "LINUX" },
"executionRoleArn": "arn:aws:iam::111111111111:role/orders-execution",
"taskRoleArn": "arn:aws:iam::111111111111:role/orders-task",
"containerDefinitions": [{
"name": "app",
"image": "111111111111.dkr.ecr.eu-west-1.amazonaws.com/orders@sha256:<digest>",
"essential": true,
"portMappings": [{ "containerPort": 8080, "protocol": "tcp" }],
"environment": [{ "name": "PORT", "value": "8080" }],
"secrets": [
{ "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:eu-west-1:111111111111:secret:orders/db-AbCdEf" },
{ "name": "API_KEY", "valueFrom": "arn:aws:ssm:eu-west-1:111111111111:parameter/orders/api-key" }
],
"healthCheck": {
"command": ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz || exit 1"],
"interval": 15, "timeout": 5, "retries": 3, "startPeriod": 30
},
"stopTimeout": 60,
"logConfiguration": {
"logDriver": "awslogs",
"options": { "awslogs-group": "/ecs/orders", "awslogs-region": "eu-west-1", "awslogs-stream-prefix": "app" }
}
}]
}
| Field | Rule |
|---|---|
cpu / memory | Must be a valid Fargate pair: 256 (512-2048), 512 (1-4 GB), 1024 (2-8 GB), 2048 (4-16 GB), 4096 (8-30 GB), larger sizes up to 16 vCPU / 120 GB |
executionRoleArn | Used by the ECS agent: pull from ECR, write logs, fetch secrets. Needs secretsmanager:GetSecretValue / ssm:GetParameters (and kms:Decrypt for CMKs) on those ARNs only |
taskRoleArn | Used by your code. Grants only what the app calls. Never reuse the execution role |
image | Pin by digest or immutable tag. :latest makes rollbacks meaningless |
healthCheck | The command must exist in the image; distroless images have no shell or wget, so add a tiny health binary or rely on the ALB check |
stopTimeout | Up to 120 s on Fargate. The app must handle SIGTERM and finish in-flight work in this window |
runtimePlatform | ARM64 is cheaper; build a multi-arch or arm64 image first |
secrets values resolve at task start only; rotating a secret needs a new deployment
(aws ecs update-service --force-new-deployment) to take effect.
Service and load balancer
- Run tasks in private subnets with
assignPublicIp: DISABLED. They need a NAT gateway or VPC endpoints (ECR API, ECR DKR, S3 gateway, CloudWatch Logs, Secrets Manager/SSM) to pull images and secrets. - Target group: type
ip, health check path that tests the app but not its dependencies (a database blip should not kill every task). Set the deregistration delay to slightly more than your longest request, often 30 s instead of the 300 s default, which otherwise slows every deploy. healthCheckGracePeriodSecondslonger than app start time, or slow starters get killed in a loop.
Deployments
| Need | Use |
|---|---|
| Default | Rolling update with deployment circuit breaker and rollback |
| Instant cutover with test traffic and fast rollback | ECS blue/green (built into the ECS deployment controller) or CodeDeploy blue/green |
| Zero extra capacity allowed | minimumHealthyPercent: 50, maximumPercent: 100, slower |
"deploymentConfiguration": {
"minimumHealthyPercent": 100,
"maximumPercent": 200,
"deploymentCircuitBreaker": { "enable": true, "rollback": true },
"alarms": { "alarmNames": ["orders-5xx-high"], "enable": true, "rollback": true }
}
Deploy workflow:
- Image built, scanned and pushed; digest recorded
- New task definition revision registered from IaC
- User confirmed deploy target (production deploys always need a clear yes)
-
aws ecs update-service --cluster prod --service orders --task-definition orders:42 -
aws ecs wait services-stable --cluster prod --services orders - Check
rolloutStateisCOMPLETED, error rate and latency normal
Scaling and cost
- Target tracking on
ECSServiceAverageCPUUtilization(60-70%) for CPU-bound services; ALBRequestCountPerTargetfor request-bound ones; queue depth per task (custom metric) for workers. - Set a minimum of 2 tasks across 2 AZs for anything user-facing.
- Fargate Spot via capacity provider strategy for interruptible work, keeping a base on on-demand:
"capacityProviderStrategy": [
{ "capacityProvider": "FARGATE", "base": 2, "weight": 1 },
{ "capacityProvider": "FARGATE_SPOT", "weight": 3 }
]
Spot tasks get a 2-minute warning: ECS emits a task state change event and sends SIGTERM. Handle SIGTERM properly or interrupted tasks drop requests.
- Compute Savings Plans cover Fargate. Right-size from Container Insights CPU/memory utilization before buying commitments.
Debugging
| Symptom | Likely cause |
|---|---|
CannotPullContainerError | No route to ECR (missing NAT/endpoints), wrong image tag/arch, execution role lacks ECR permissions |
ResourceInitializationError: unable to pull secrets | Execution role lacks GetSecretValue/KMS, or no route to Secrets Manager/SSM |
Essential container in task exited with exit 137 | Out of memory (check reason: OutOfMemoryError) or killed after stopTimeout |
| Exit 1 right after start | App crash; read logs in /ecs/orders |
| Tasks healthy in ECS, unhealthy in target group | Health check path/port mismatch, security group blocks ALB to task port |
Deployment stuck IN_PROGRESS | New tasks never pass health checks; circuit breaker off |
exec format error | arm64 image on X86_64 platform or the reverse |
ECS Exec into a running task (requires enableExecuteCommand: true on the service, SSM
permissions on the task role, and the Session Manager plugin locally):
aws ecs execute-command --cluster prod --task <task-id> --container app \
--interactive --command "/bin/sh"
Treat exec sessions in production as privileged access; ask before running commands that change state.
Gotchas
- The
awslogslog group must exist or the execution role needslogs:CreateLogGroupandawslogs-create-group: "true". Set retention on it. - Environment variables are visible in the task definition to anyone with describe
access. Anything secret goes in
secrets. - Fargate ephemeral storage is 20 GiB by default (configurable up to 200 GiB) and is lost when the task stops. Use EFS or S3 for state.
- A service with
desiredCountmanaged by autoscaling gets reset by IaC on every deploy unless the IaC ignores that field. - Scaling to zero or deleting a service in production needs explicit user confirmation.
Output
Report: task definition revision and image digest, roles and the permissions changed,
deployment settings, the verification results (services-stable, rollout state, health
metrics), and any cost effect (task size, Spot share).
What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "AWS ECS Fargate"
short_description: "Container deploys that roll themselves back"
default_prompt: "$aws-ecs-fargate Move this Dockerized API onto ECS Fargate behind an ALB" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.