aws-debug
Debug AWS ECS/Fargate deployment failures, service crashes, and pipeline issues. Covers the full investigation workflow from pipeline to container logs.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Debug AWS ECS/Fargate deployment failures, service crashes, and pipeline issues. Covers the full investigation workflow from pipeline to container logs.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | aws-debug |
| description | Debug AWS ECS/Fargate deployment failures, service crashes, and pipeline issues. Covers the full investigation workflow from pipeline to container logs. |
| version | 1.0.0 |
You are debugging an AWS deployment failure. Follow the methodology below systematically. Do not jump to conclusions — gather evidence from every layer before proposing fixes.
Rule: Always state which environment / account / region / pipeline / service you are currently inspecting, so the user can follow along.
Rule: After each major investigation step, summarize: what's healthy, what's suspicious, what to inspect next.
Rule: Compare stage vs prod before concluding. A problem that exists in one environment but not the other is a configuration drift issue, not a code issue.
You cannot debug what you cannot see. Before investigating any failure:
logConfiguration. If a container has no log driver, failed tasks produce zero CloudWatch output. Fix this immediately — nothing else matters until logs exist.aws ecs describe-task-definition --task-definition <family> --region <region> \
--query 'taskDefinition.containerDefinitions[*].{name:name,logConfig:logConfiguration}'
{
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/<service-name>",
"awslogs-create-group": "true",
"awslogs-region": "<region>",
"awslogs-stream-prefix": "ecs"
}
}
This project has no IaC. All infrastructure is configured via the AWS Console. Reverse-engineer the architecture before debugging:
# Pipeline structure (source trigger, build project, deploy actions)
aws codepipeline get-pipeline --name <pipeline> --region <region>
# Build configuration (inline buildspec, env vars, which Dockerfiles)
aws codebuild batch-get-projects --names <project> --region <region> \
--query 'projects[0].source.buildspec'
# What the pipeline ACTUALLY used (may differ from current project config)
aws codebuild batch-get-builds --ids <build-id> --region <region> \
--query 'builds[0].source.buildspec'
# ECR repositories and image tags
aws ecr describe-repositories --region <region>
aws ecr describe-images --repository-name <repo> --region <region> \
--query 'imageDetails[?imageTags!=`null`].[imageTags,imagePushedAt]'
# ECS services, task definitions, ALB target groups
aws ecs describe-services --cluster <cluster> --services <svc1> <svc2> --region <region>
aws elbv2 describe-target-groups --region <region>
Key things to verify:
pipeline.triggers[].gitConfiguration.push[].branches)For each failed pipeline execution:
# List recent executions
aws codepipeline list-pipeline-executions --pipeline-name <pipeline> --region <region> --max-items 5
# Check which stage/action failed
aws codepipeline list-action-executions --pipeline-name <pipeline> \
--filter pipelineExecutionId=<id> --region <region> \
--query 'actionExecutionDetails[*].{stage:stageName,action:actionName,status:status,summary:output.executionResult.externalExecutionSummary}'
If Build failed: Check CodeBuild logs. Common causes: Docker build failure, ECR push auth failure, buildspec syntax.
If Deploy failed: The ECS deployment didn't complete. Check for:
This is where most issues hide. Check three layers:
aws ecs describe-services --cluster <cluster> --services <svc> --region <region> \
--query 'services[0].{deployConfig:deploymentConfiguration,deployments:deployments[*].{taskDef:taskDefinition,running:runningCount,desired:desiredCount,failed:failedTasks,rolloutState:rolloutState},events:events[0:10]}'
Read events chronologically. Look for:
aws ecs describe-tasks --cluster <cluster> --tasks <task-id> --region <region> \
--query 'tasks[0].{stopCode:stopCode,stopReason:stoppedReason,containers:containers[*].{name:name,exitCode:exitCode,reason:reason}}'
Decode the results:
| exitCode | container reason | Meaning |
|---|---|---|
| 0 | — | Graceful shutdown (SIGTERM from ECS) |
| 1 | — | Application error (unhandled exception) |
| 137 | OutOfMemoryError: container killed due to memory usage | OOM kill. Increase task memory. |
| 137 | — (no OOM message) | SIGKILL — killed externally (health check or ECS stop) |
| null | CannotPullContainerError | Image doesn't exist in ECR or IAM can't pull |
# Find log streams for recent tasks
aws logs describe-log-streams --log-group-name /ecs/<service> \
--order-by LastEventTime --descending --max-items 5 --region <region>
# Read the END of a crashed task's logs (crash reason is always at the tail)
aws logs get-log-events --log-group-name /ecs/<service> \
--log-stream-name "<stream>" --no-start-from-head --limit 30 --region <region> \
--query 'events[*].[timestamp,message]' --output text
Read logs tail-first. The crash reason is always in the last few lines. Common patterns:
Emitted 'error' event on ... → unhandled Node.js EventEmitter error, process killedJavaScript heap out of memory → Node.js OOM (different from container OOM)npm notice as the very last line → process exited (npm prints this on exit)Health check misconfiguration is the most common cause of deployment failures for slow-starting apps. There are TWO independent health check systems:
aws elbv2 describe-target-groups --region <region> \
--query 'TargetGroups[*].{name:TargetGroupName,port:Port,path:HealthCheckPath,interval:HealthCheckIntervalSeconds,healthy:HealthyThresholdCount,unhealthy:UnhealthyThresholdCount}'
Math: UnhealthyThreshold * HealthCheckIntervalSeconds = max seconds before ALB declares target dead. This MUST exceed the application's cold-start time.
# Check current target health
aws elbv2 describe-target-health --target-group-arn <arn> --region <region>
Defined in the task definition's containerDefinitions[].healthCheck. Has its own startPeriod (grace period before checks begin), interval, retries, and timeout.
Common misconfiguration: ALB unhealthy threshold too low while the app takes 60-90 seconds to start. The ALB kills the target before the app is ready, ECS sees the task as failed, and the deployment rolls back or loops.
aws ecs describe-services --cluster <cluster> --services <svc> --region <region> \
--query 'services[0].deploymentConfiguration'
Critical settings:
| Setting | Bad value | Good value | Why |
|---|---|---|---|
| maximumPercent | 100 | 200 | 100 means ECS can't start a new task alongside the old one |
| minimumHealthyPercent | 0 | 100 | 0 means ECS may kill all tasks before new ones are ready |
| circuit breaker | disabled | enabled + rollback | Without it, a bad deployment loops forever |
The deadly combo: maxPercent:100 + minHealthyPercent:0 guarantees downtime on every deployment. ECS stops the old task first (minHealthy=0 allows it), then starts the new one (maxPercent=100 means only 1 at a time). If the new task fails, there are zero running tasks.
After investigating both environments, diff every configurable surface:
| Check | Command |
|---|---|
| Task def memory/CPU | describe-task-definition |
| Task def env vars | describe-task-definition → containerDefinitions[].environment |
| Task def log config | describe-task-definition → containerDefinitions[].logConfiguration |
| ALB health thresholds | describe-target-groups |
| Deployment config | describe-services → deploymentConfiguration |
| Pipeline trigger branch | get-pipeline → triggers |
| ECR image tags | describe-images |
Any difference is a potential drift bug. Flag every discrepancy.
Always fix in this order:
batch-get-builds to see the actual buildspec that ran, not just the current project config.exitCode: 137 + reason: OutOfMemoryError on the task description. Logs will show the process mid-operation with no error, then nothing.npm notice as the last log line is Node.js/npm printing on process exit. It means the process ended — look at what came before it for the actual cause.error events in Node.js kill the entire process with no catch. Libraries like fluent-ffmpeg emit these. Always check for missing .on('error', ...) handlers.