Task Definitions & Container Definitions
The task definition is the center of ECS. Everything else - services, deployments, scaling - runs copies of a task definition. Get the blueprint right and the rest of ECS is wiring.
Busque em todas as páginas da documentação
The task definition is the center of ECS. Everything else - services, deployments, scaling - runs copies of a task definition. Get the blueprint right and the rest of ECS is wiring.
A task definition has two layers: task-level settings (CPU, memory, IAM roles, network mode) and a list of container definitions, one per container in the task. This page shows how to register them via the SDK, size Fargate correctly, and inject configuration and secrets the safe way.
RegisterTaskDefinition writes a new immutable revision. The task-level fields wrap a containerDefinitions array.
# --- Python (boto3) ---
import boto3
ecs = boto3.client("ecs", region_name="us-east-1")
resp = ecs.register_task_definition(
family="api",
requiresCompatibilities=["FARGATE"],
networkMode="awsvpc",
cpu="512",
memory="1024",
executionRoleArn="arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
taskRoleArn="arn:aws:iam::111122223333:role/apiTaskRole",
containerDefinitions=[{
"name": "api",
"image": "111122223333.dkr.ecr.us-east-1.amazonaws.com/api:1.4.0",
"essential": True,
"portMappings": [{"containerPort": 8080, "protocol": "tcp"}],
}],
)
print(resp["taskDefinition"]["taskDefinitionArn"])// --- TypeScript (AWS SDK v3) ---
import { ECSClient, RegisterTaskDefinitionCommand } from "@aws-sdk/client-ecs";
const ecs = new ECSClient({ region: "us-east-1" });
const resp = await ecs.send(new RegisterTaskDefinitionCommand({
family: "api",
requiresCompatibilities: ["FARGATE"],
networkMode: "awsvpc",
cpu: "512",
memory: "1024",
executionRoleArn: "arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
taskRoleArn: "arn:aws:iam::111122223333:role/apiTaskRole",
containerDefinitions: [{
name: "api",
image: "111122223333.dkr.ecr.us-east-1.amazonaws.com/api:1.4.0",
essential: true,
portMappings: [{ containerPort: 8080, protocol: "tcp" }],
}],
}));
console.log(resp.taskDefinition?.taskDefinitionArn);Two roles appear here and they are not the same thing. The execution role is used by the Fargate agent to pull the image, write logs, and fetch secrets before your code runs. The task role is assumed by your application at runtime for its own AWS API calls.
A realistic container definition carries environment variables, secrets, and a log driver. This is the shape you deploy in practice.
# --- Python (boto3) ---
import boto3
ecs = boto3.client("ecs", region_name="us-east-1")
resp = ecs.register_task_definition(
family="api",
requiresCompatibilities=["FARGATE"],
networkMode="awsvpc",
cpu="512",
memory="1024",
executionRoleArn="arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
taskRoleArn="arn:aws:iam::111122223333:role/apiTaskRole",
containerDefinitions=[{
"name": "api",
"image": "111122223333.dkr.ecr.us-east-1.amazonaws.com/api:1.4.0",
"essential": True,
"portMappings": [{"containerPort": 8080}],
"environment": [
{"name": "LOG_LEVEL", "value": "info"},
{"name": "REGION", "value": "us-east-1"},
],
"secrets": [
{"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:111122223333:secret:prod/db-AbCdEf"},
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/api",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "api",
},
},
}],
)
print(resp["taskDefinition"]["revision"])// --- TypeScript (AWS SDK v3) ---
import { ECSClient, RegisterTaskDefinitionCommand } from "@aws-sdk/client-ecs";
const ecs = new ECSClient({ region: "us-east-1" });
const resp = await ecs.send(new RegisterTaskDefinitionCommand({
family: "api",
requiresCompatibilities: ["FARGATE"],
networkMode: "awsvpc",
cpu: "512",
memory: "1024",
executionRoleArn: "arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
taskRoleArn: "arn:aws:iam::111122223333:role/apiTaskRole",
containerDefinitions: [{
name: "api",
image: "111122223333.dkr.ecr.us-east-1.amazonaws.com/api:1.4.0",
essential: true,
portMappings: [{ containerPort: 8080 }],
environment: [
{ name: "LOG_LEVEL", value: "info" },
{ name: "REGION", value: "us-east-1" },
],
secrets: [
{ name: "DB_PASSWORD",
valueFrom: "arn:aws:secretsmanager:us-east-1:111122223333:secret:prod/db-AbCdEf" },
],
logConfiguration: {
logDriver: "awslogs",
options: {
"awslogs-group": "/ecs/api",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "api",
},
},
}],
}));
console.log(resp.taskDefinition?.revision);The secrets entries reference an ARN, not a value. At task start the execution role fetches each secret and injects it as an environment variable inside the container - the plaintext never appears in the task definition, the API, or CloudTrail.
Fargate does not accept arbitrary CPU/memory combinations. Each CPU value permits a fixed memory range. Both are strings in RegisterTaskDefinition.
| CPU (vCPU) | Valid memory (MiB) |
|---|---|
| 256 (.25) | 512, 1024, 2048 |
| 512 (.5) | 1024 through 4096, in 1 GB steps |
| 1024 (1) | 2048 through 8192, in 1 GB steps |
| 2048 (2) | 4096 through 16384, in 1 GB steps |
| 4096 (4) | 8192 through 30720, in 1 GB steps |
| 8192 (8) | 16384 through 61440, in 4 GB steps |
| 16384 (16) | 32768 through 122880, in 8 GB steps |
An invalid pair (say cpu="256", memory="4096") is rejected at registration with a ClientException. Size for the container's real footprint plus headroom; oversizing burns money every second the task runs.
Fargate supports exactly one network mode: awsvpc. Each task gets its own elastic network interface, a private IP in your subnet, and its own security groups - a task is a first-class VPC citizen. On the EC2 launch type you also have bridge, host, and none, but for Fargate awsvpc is required and container ports are reached directly on the task's ENI (no host port mapping).
The distinction trips up most newcomers.
executionRoleArn) is used before and around your container: pulling the image from ECR, creating the log stream, and reading secrets from Secrets Manager or SSM. It needs ecr:GetDownloadUrlForLayer, logs:CreateLogStream/PutLogEvents, and secretsmanager:GetSecretValue for the secrets you inject.taskRoleArn) is assumed by your application code through the task's metadata endpoint. It carries the permissions your service needs - reading an S3 bucket, writing to DynamoDB - and should be least-privilege.If image pulls or secrets fail at startup, the execution role is almost always the cause. If your app gets AccessDenied at runtime, look at the task role.
The secrets field accepts two source types by ARN. A Secrets Manager secret ARN injects the whole secret value, or a single JSON key with the arn:...:secret:name:json-key:: syntax. An SSM Parameter Store SecureString ARN works the same way. Either keeps the value out of the task definition body. Plaintext config that is not sensitive belongs in environment; anything sensitive belongs in secrets.
RegisterTaskDefinition rejects combinations outside the Fargate table; consult the table before sizing.AccessDenied is a task-role problem.environment exposes it in the task definition and CloudTrail. Use secrets with an ARN.awslogs driver does not create the group unless you add "awslogs-create-group": "true"; otherwise startup fails on a missing group.RegisterTaskDefinition for the same family produces a new revision; deploy by pointing at the new one.awsvpc. Passing bridge or host fails validation.secrets with an ARN is the safe path.containerDefinitions.Register through the SDK when definitions are generated programmatically or by a deploy tool; otherwise let IaC own the static blueprint and use the SDK to run and update.
A task definition is the whole blueprint, including task-level CPU, memory, roles, and network mode. Container definitions are the per-container entries inside it - image, ports, env, secrets, and log config.
The ECS API models them as strings (for example "512"). They must also form a valid Fargate pair; each CPU value permits only a specific memory range.
The execution role is used by Fargate to pull images, write logs, and read secrets before your app runs. The task role is assumed by your application code at runtime for its own AWS API calls.
Store it in Secrets Manager or an SSM SecureString, then reference its ARN in the container's secrets field with valueFrom. Fargate injects it as an env var at startup without exposing the value.
Only awsvpc. Each task gets its own elastic network interface, private IP, and security groups. The bridge, host, and none modes are EC2 launch type only.
No. Revisions are immutable. Call RegisterTaskDefinition again for the same family to create a new revision, then update your service or run task to use it.
Almost always the execution role. It needs ECR pull permissions (or public registry access) and, for private images, the right repository policy. Image-pull errors show up in the task's stopped reason.
Not by default. Either create the CloudWatch log group ahead of time or add "awslogs-create-group": "true" to the driver options, along with logs:CreateLogGroup on the execution role.
Yes. Add multiple entries to containerDefinitions. They share the task's network namespace and lifecycle, which is how sidecars like log routers or proxies are deployed.
Append the key to the Secrets Manager ARN using the :json-key:: suffix in valueFrom. ECS extracts that single field instead of the whole secret document.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Revisado por Chris St. John·Última atualização: 23 de jul. de 2026