In the previous lessons, you've built a solid foundation with ECS clusters and learned how to configure capacity providers for optimal cost and performance. However, having a cluster is just the first step — you still need to tell ECS what containers to run and how to configure them. This is where task definitions come into play. A task definition is essentially a blueprint that describes one or more containers that should run together as a unit, telling ECS exactly what image to use, how much CPU and memory to allocate, which ports to expose, and how to handle logging.
In this lesson, you'll create your first production-ready task definition that pulls a container image from Amazon Elastic Container Registry (ECR), exposes port 3000 for web traffic, and ships application logs to CloudWatch for monitoring and debugging. This represents a realistic scenario where you're deploying a custom web application that needs secure image storage, network connectivity, and centralized logging.
Your main goal is to register a Fargate task definition using the AWS CLI that incorporates these three critical components: ECR integration for private container images, port mapping for network access, and CloudWatch logging for operational visibility. By the end of this lesson, you'll have a registered task definition ready to run on your Fargate cluster, bridging the gap between infrastructure and actual application deployment.
Before your task definition can send logs to CloudWatch, the log group must exist. CloudWatch Logs organizes log data into groups, which act as containers for log streams from your running containers. Each container instance will create its own log stream within the group, making it easy to track logs from multiple instances of the same application.
You'll create the log group using a command that's safe to run multiple times. This idempotent approach means you can execute the command repeatedly without causing errors, which is useful when automating deployments or when multiple team members are working with the same infrastructure.
Run this command in your terminal:
The command creates a log group at /ecs/my-web-app. The 2>/dev/null || true portion handles the case where the log group already exists — it suppresses the error message and ensures the command always exits successfully.
When you run this command for the first time, CloudWatch will create the log group and you won't see any output. If you run it again, the command will attempt to create the log group, receive an error that it already exists, suppress that error, and continue successfully. This pattern is commonly used in infrastructure automation where you want commands to be safe to run repeatedly.
You can verify that your log group was created by listing all log groups with a specific prefix:
This will show you all log groups that start with /ecs/, including the one you just created.
A Fargate task definition requires several specific configuration keys that tell ECS how to run your containers in the serverless environment. Unlike EC2 launch types, where you manage the underlying instances, Fargate needs explicit resource specifications and network configuration to provision the right amount of compute capacity.
Create a file called taskdef.json with the basic Fargate-compatible structure:
Each field in this skeleton serves a specific purpose for Fargate compatibility:
- family: Acts as a name for your task definition, and ECS will automatically version your task definitions as you make changes. Each time you register a new revision of the same family, ECS increments the revision number, allowing you to track changes and roll back if needed.
- networkMode: Must be set to
awsvpcfor Fargate tasks. This mode gives each task its own elastic network interface with a private IP address, providing network isolation and allowing you to apply security groups directly to your tasks. - requiresCompatibilities: Explicitly declares that this task definition is designed for Fargate. While you could specify both
FARGATEandEC2to make the task definition compatible with both launch types, specifying onlyFARGATEensures that the configuration is optimized for the serverless model. - cpu and memory: Define the compute resources allocated to your task. Fargate uses specific combinations of CPU and memory values — you can't choose arbitrary amounts. The
256CPU units represent 0.25 vCPU, and512MB of memory is the minimum memory allocation for this CPU level. These resources are allocated to the entire task and shared among all containers defined within it. - executionRoleArn: Specifies the IAM role that Fargate uses to pull your container image and write logs. Note: In this practice environment, this role has been pre-configured for you. In a fresh AWS account, you would create this role and attach the AmazonECSTaskExecutionRolePolicy managed policy to allow ECS to perform these tasks on your behalf. Remember to replace 123456789012 with your actual AWS account ID.
This skeleton provides the foundation for a Fargate-compatible task definition that can be extended with container specifications.
Within your task definition, the containerDefinitions array describes the actual containers that will run as part of your task. Each container definition specifies the image location, resource requirements, network configuration, and logging setup. For this lesson, you'll define a single web container that serves traffic on port 3000 and sends logs to CloudWatch.
Add the container definition to your taskdef.json file within the containerDefinitions array:
Remember to replace 123456789012 with your actual AWS account ID in both the executionRoleArn and the image URI. You can find your account ID by running aws sts get-caller-identity --query Account --output text.
The key configuration elements work together to define your container's behavior:
- name: Provides an identifier for this container within the task. If your task definition contained multiple containers, each would need a unique name.
- image: Points to your ECR image URI, including the repository name and tag. The format is
<account-id>.dkr.ecr.<region>.amazonaws.com/<repository-name>:<tag>. In this example, we're usingmy-web-app:latest, but in production environments, you would typically use specific version tags likemy-web-app:v1.2.3for better deployment control and rollback capabilities. - essential: Indicates that this container is critical to the task's operation. If an essential container stops or fails, ECS will stop the entire task. Since this is your main web application container, marking it as essential ensures that task failures are detected and handled appropriately.
- portMappings: Defines which ports your container exposes for network traffic. In this configuration, you're exposing
containerPort3000 using the TCP protocol. With Fargate'sawsvpcnetwork mode, this port will be directly accessible on the task's network interface. - logConfiguration: Tells ECS how to handle your container's stdout and stderr output. The
awslogsdriver sends logs directly to CloudWatch Logs, with options specifying the AWS region, log group, and stream prefix for organized log management.
This configuration creates a complete container definition that integrates seamlessly with AWS services for image storage, networking, and logging.
With your task definition file complete, you're ready to register it with ECS. The registration process stores your configuration in ECS and makes it available for running tasks or creating services.
Run this command in your terminal to register your task definition:
The aws ecs register-task-definition command takes your JSON file and creates a new task definition in ECS. The --cli-input-json file:// syntax tells the AWS CLI to read the JSON input from a file rather than from command line parameters. When successful, this command returns detailed information about the registered task definition, including its ARN and revision number.
The output will show you the complete task definition that ECS has registered, including any default values that ECS added automatically. You'll see a response similar to this:
When you no longer need a specific revision of a task definition, you can deregister it. Deregistering a task definition marks it as INACTIVE, which prevents it from being used to run new tasks or create new services. However, it does not affect any existing tasks or services that are already running with that revision. This is a safety measure that allows you to phase out old configurations without disrupting current deployments.
To deregister the task definition revision you just created, you can use the deregister-task-definition command. You need to specify the exact family and revision number.
When successful, the command will return the full task definition object with its new status. You can verify that the status is now INACTIVE by running the describe-task-definition command again.
In the output, you will see the status field has changed from ACTIVE to INACTIVE.
This confirms that the task definition revision can no longer be used for new deployments.
In this lesson, you managed a Fargate task definition's lifecycle by registering a new revision. This production-ready blueprint integrates ECR for images, port 3000 for networking, and CloudWatch for logs. You learned to structure it with Fargate requirements like awsvpc network mode and an execution role.
The task definition you registered serves as a blueprint that tells ECS exactly how to run your containerized web application — pulling the my-web-app:latest image from ECR, allocating 0.25 vCPU and 512 MB of memory, exposing port 3000, and streaming logs to CloudWatch with the ecs prefix.
In the upcoming practice exercises, you'll take the next step by actually running tasks based on this definition on your ECS cluster. You'll configure the networking components that Fargate needs, including VPC subnets and security groups, and see your containerized application come to life while monitoring it through CloudWatch logs.
