Run a container on ECS Fargate

Run a container image on Amazon ECS with Fargate from the CLI or TypeScript — the two IAM roles and which one does what, a valid CPU/memory pair, a task definition with logs, the network route the task needs to pull its image, and how to read why a task stopped.

markdownguidev4GB9S-RJKFstage: buildtrust: officialservice: ecsinterface: cliinterface: sdkECS Fargaterun-taskregister-task-definitiontask execution roletask roleecs-tasks.amazonaws.comAmazonECSTaskExecutionRolePolicyawsvpcassignPublicIpFargate CPU memoryawslogsRunTaskCommanddescribe-tasksstoppedReason

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

**You need a task definition with a valid CPU/memory pair and an execution role, a cluster, and subnets whose route lets the task reach the image registry.** A missing route is the most common first failure: the task stops before your code starts.

1. Two roles, two jobs

RoleUsed byNeedsSet as
Task execution roleThe ECS agent and Fargate, before and around your containerPull from ECR, write logs, fetch secrets for injectionexecutionRoleArn
Task roleYour application codeWhatever the app calls: S3, DynamoDB, …taskRoleArn

Both trust ecs-tasks.amazonaws.com. Start the execution role from the AWS managed AmazonECSTaskExecutionRolePolicy:

aws iam create-role --role-name ecsTaskExecutionRole \
  --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ecs-tasks.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name ecsTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

If the task definition injects secrets, add these to the execution role: secretsmanager:GetSecretValue for Secrets Manager, ssm:GetParameters for Parameter Store, and kms:Decrypt if a customer managed key encrypts them. An app that gets AccessDenied at runtime needs the permission on the task role instead. Whoever registers the task definition or runs the task needs iam:PassRole on both roles.

2. Pick CPU and memory from the table

Fargate accepts only fixed combinations (reference/ecs/roles-and-networking.md). For example, 256 CPU units take 512, 1024 or 2048 MiB, and 1024 units take 2048–8192 MiB. An invalid pair fails registration with Invalid 'cpu' setting for task (Terraform shows No Fargate configuration exists for given values).

3. Register the task definition

{
  "family": "<app>",
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "512",
  "memory": "1024",
  "runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" },
  "executionRoleArn": "arn:aws:iam::<account>:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::<account>:role/<app>-task-role",
  "containerDefinitions": [{
    "name": "app",
    "image": "<account>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>",
    "essential": true,
    "portMappings": [{ "containerPort": 8080 }],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": { "awslogs-group": "/ecs/<app>", "awslogs-region": "<region>", "awslogs-stream-prefix": "app" }
    }
  }]
}
aws logs create-log-group --log-group-name /ecs/<app>
aws ecs register-task-definition --cli-input-json file://taskdef.json
aws ecs create-cluster --cluster-name <cluster>

Create the log group first. Otherwise the task fails at start with a ResourceInitializationError. The alternative is the log option awslogs-create-group: "true": it defaults to false, and it needs logs:CreateLogGroup in the execution role's policy (per the ECS API model's log-configuration documentation). The image architecture must match cpuArchitecture. Pin a tag or digest, not latest.

4. Choose the network path

Fargate tasks use awsvpc networking: each task gets its own network interface in your subnet. To pull an image, the task needs one of:

SubnetSettingWorks because
Private, route to a NAT gateway (zonal, or regional with no public subnet needed)assignPublicIp=DISABLEDNAT reaches ECR
Private, no NATassignPublicIp=DISABLED + interface endpoints com.amazonaws.<region>.ecr.api and ecr.dkr + S3 gateway endpoint (+ logs and secretsmanager/ssm endpoints if used)Image layers come from S3, so ECR endpoints alone are not enough (patterns/ecs-to-ecr.md)
Public, route to an internet gatewayassignPublicIp=ENABLEDThe task's public IP reaches ECR

A public subnet without a public IP, or a private subnet with neither NAT nor endpoints, fails to pull. You can attach up to 16 subnets and 5 security groups. Security groups need outbound HTTPS (443).

5. Run it

aws ecs run-task --cluster <cluster> --launch-type FARGATE --task-definition <app> \
  --network-configuration 'awsvpcConfiguration={subnets=[<subnet>],securityGroups=[<sg>],assignPublicIp=DISABLED}'
import { ECSClient, RunTaskCommand, DescribeTasksCommand } from "@aws-sdk/client-ecs";

const ecs = new ECSClient({ region: "<region>" });

const run = await ecs.send(new RunTaskCommand({
  cluster: "<cluster>",
  taskDefinition: "<family>:<revision>",
  launchType: "FARGATE",
  count: 1,
  networkConfiguration: {
    awsvpcConfiguration: {
      subnets: ["<subnet id>"],
      securityGroups: ["<sg id>"],
      assignPublicIp: "DISABLED", // ENABLED only in a public subnet with no NAT
    },
  },
}));
for (const f of run.failures ?? []) console.error(f.arn, f.reason, f.detail);

const arn = run.tasks?.[0]?.taskArn;
if (arn) {
  const d = await ecs.send(new DescribeTasksCommand({ cluster: "<cluster>", tasks: [arn] }));
  const t = d.tasks?.[0];
  console.log(t?.lastStatus, t?.stoppedReason, t?.containers?.map((c) => [c.name, c.exitCode, c.reason]));
}

run-task can succeed as an API call yet return entries in failures. Always check them. For a long-running app behind a load balancer, use create-service instead. The target group's target type must be ip.

6. If the task stops

aws ecs describe-tasks --cluster <cluster> --tasks <task-id> \
  --query 'tasks[0].{stopped:stoppedReason,code:stopCode,containers:containers[].[name,exitCode,reason]}'

Stopped tasks remain visible for about one hour, so look quickly or rely on the logs.

  • stoppedReason starts with ResourceInitializationError or CannotPullContainerError: the role or the network path (steps 1 and 4).
  • A container exit code: 137 means killed (out of memory or stop timeout), 139 a segfault, 255 an ENTRYPOINT/CMD failure.

Look the message up in reference/ecs/errors.md.

Read this document as markdown — the same bytes an agent receives.