What is Amazon ECS?
Amazon Elastic Container Service (ECS) is a fully managed container orchestration service that allows you to run and scale containerized applications on AWS. It handles the heavy lifting of scheduling containers across a cluster of virtual machines, managing their lifecycle, and integrating deeply with other AWS services like networking, storage, monitoring, and security.
ECS operates using two main launch types:
- EC2 Launch Type β You manage the underlying EC2 instances that form the cluster. Containers are placed on these instances according to resource availability and constraints.
- Fargate Launch Type β AWS manages the infrastructure completely. You only define the task (the container spec) and the network configuration, and ECS runs it without you provisioning or managing any servers.
Core ECS concepts include:
- Cluster β A logical grouping of container instances (EC2) or a serverless boundary (Fargate) where tasks run.
- Task Definition β A JSON blueprint describing which container images to run, resource allocations (CPU/memory), networking mode, logging, and IAM roles.
- Task β An instantiation of a task definition that runs one or more containers together on a single host.
- Service β Ensures a desired number of tasks are running at all times, supports rolling deployments, load balancing, and auto-scaling.
Why ECS Matters
π Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →ECS solves the operational complexity of container deployment at scale. It matters for modern development because:
- Serverless Container Experience (Fargate) β Removes the need to patch, scale, or maintain EC2 instances. You pay only for the vCPU and memory your containers request, making cost management straightforward.
- Deep AWS Integration β ECS works seamlessly with IAM for security, CloudWatch for logging and monitoring, Application Load Balancer for traffic routing, and AWS Auto Scaling for dynamic capacity adjustments.
- Predictable Deployments β Rolling updates and blue/green deployments (via CodeDeploy) allow safe, controlled releases.
- Security Posture β Tasks can be assigned fine-grained IAM roles, network isolation via VPC, and encrypted environment variables. Containers run without privileged access by default.
- Flexibility β You can mix EC2 and Fargate across different services, and choose from a wide range of CPU architectures (x86, ARM/Graviton).
Setting Up ECS: Prerequisites
Before creating your ECS resources, ensure you have:
- An AWS account with appropriate permissions (at minimum, permissions for
ecs:*,iam:*,ec2:*,elasticloadbalancing:*,logs:*). - A VPC with at least two subnets in different Availability Zones for high availability. For Fargate tasks, these subnets must have internet access (via NAT gateway or public subnet with direct internet) if containers need to pull images or make outbound calls.
- A security group that allows relevant traffic (e.g., inbound from the load balancer on port 80).
- AWS CLI installed and configured, or access to AWS Management Console. The examples below use the AWS CLI.
Step-by-Step Configuration Guide
1. Create an ECS Cluster
A cluster is a namespace for your tasks and services. For a Fargate-only cluster (no EC2 instances), creation is trivial:
aws ecs create-cluster \
--cluster-name my-fargate-cluster \
--capacity-providers FARGATE \
--default-capacity-provider-strategy capacityProvider=FARGATE,weight=1
If you plan to use EC2 launch type, you need to create the cluster and then register EC2 instances with the container agent. For simplicity, the rest of this guide assumes Fargate, but the concepts translate directly to EC2.
2. Define a Task Definition
Create a file named task-definition.json with a complete container specification. Below is a production-ready example running a simple web application:
{
"family": "my-web-app",
"networkMode": "awsvpc",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/myAppTaskRole",
"cpu": "256",
"memory": "512",
"requiresCompatibilities": ["FARGATE"],
"containerDefinitions": [
{
"name": "web-container",
"image": "public.ecr.aws/mycompany/webapp:latest",
"essential": true,
"portMappings": [
{
"containerPort": 8080,
"protocol": "tcp"
}
],
"environment": [
{
"name": "ENVIRONMENT",
"value": "production"
},
{
"name": "DB_HOST",
"value": "rds-prod.cluster-cxjxxxxx.us-east-1.rds.amazonaws.com"
}
],
"secrets": [
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/dbpassword-AbCdEf"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "ecs/webapp-logs",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "web"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3
}
}
]
}
Key details in this definition:
networkMode: awsvpcβ Required for Fargate; gives each task its own elastic network interface.executionRoleArnβ The IAM role that grants ECS agent permissions to pull images and stream logs. It must includeecs:CreateLogStream,logs:PutLogEvents, and permissions for the image registry.taskRoleArnβ The IAM role your container code assumes. For example, to access S3 or DynamoDB.secretsβ Injects sensitive values from AWS Secrets Manager or SSM Parameter Store directly into environment variables.healthCheckβ Container-level health check; ECS will restart the container if it fails.
3. Register the Task Definition
Upload the JSON to ECS. This creates a revision (e.g., my-web-app:1):
aws ecs register-task-definition \
--cli-input-json file://task-definition.json
Every registration increments the revision number. Services can then reference a specific revision or use the family alone to pick the latest active revision.
4. Create a Service
Now you can create a service that maintains a desired number of tasks, optionally behind a load balancer. The following command creates a service with Fargate networking, public IP assignment (for internet access), and associates it with an Application Load Balancer target group:
aws ecs create-service \
--cluster my-fargate-cluster \
--service-name webapp-service \
--task-definition my-web-app:1 \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=["subnet-abc123","subnet-def456"],securityGroups=["sg-xyz789"],assignPublicIp=ENABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/webapp-tg/abcd1234,containerName=web-container,containerPort=8080" \
--deployment-configuration "maximumPercent=200,minimumHealthyPercent=100" \
--enable-execute-command
Breakdown of important parameters:
- desired-count β Number of task copies to run.
- network-configuration β The subnets must be in your VPC; assignPublicIp=ENABLED is typical for Fargate tasks that need to pull images or serve public traffic. If you use private subnets with a NAT gateway, you can set it to DISABLED.
- load-balancers β Links the container port to a target group. The target group must already exist (see next section).
- deployment-configuration β Controls how many tasks can be started/stopped during a rolling update (200% max, 100% min healthy).
- enable-execute-command β Allows ECS Exec to open an interactive shell into containers for debugging.
5. Configure Load Balancing (Optional)
For production workloads, a load balancer distributes traffic and enables zero-downtime deployments. Here's how to set up an Application Load Balancer and target group, then link them to the ECS service:
First, create the target group:
aws elbv2 create-target-group \
--name webapp-tg \
--protocol HTTP \
--port 8080 \
--target-type ip \
--vpc-id vpc-xxxxxxxx
Note: target-type ip is mandatory for Fargate tasks because they get IP addresses directly, not instance-based targets.
Create the Application Load Balancer (requires at least two public subnets):
aws elbv2 create-load-balancer \
--name webapp-alb \
--type application \
--subnets subnet-public1 subnet-public2 \
--security-groups sg-alb-public
Create a listener that forwards to the target group:
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/webapp-alb/1234567890abcdef \
--protocol HTTP \
--port 80 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/webapp-tg/abcd1234
After the service is created with the load-balancers parameter, ECS automatically registers and deregisters task IPs with the target group as tasks start and stop.
6. Set Up Auto Scaling
ECS service auto scaling uses Application Auto Scaling. You can scale based on CPU, memory, or custom CloudWatch metrics. The following registers the service as a scalable target and attaches a CPU-based scaling policy:
# Register scalable target
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/my-fargate-cluster/webapp-service \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 2 \
--max-capacity 10
# Create scaling policy (CPU target tracking)
aws application-autoscaling put-scaling-policy \
--service-namespace ecs \
--resource-id service/my-fargate-cluster/webapp-service \
--scalable-dimension ecs:service:DesiredCount \
--policy-name cpu-target-tracking \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration file://scaling-policy.json
Example content of scaling-policy.json:
{
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
},
"ScaleOutCooldown": 120,
"ScaleInCooldown": 120
}
This will keep average CPU around 70%, adding or removing tasks as needed. Similar policies exist for memory and request counts per target (ALBRequestCountPerTarget).
7. Logging and Monitoring
In the task definition we specified awslogs as the log driver. Before using it, the CloudWatch log group must exist:
aws logs create-log-group --log-group-name ecs/webapp-logs
Ensure the execution role has permissions like:
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:us-east-1:123456789012:log-group:ecs/webapp-logs:*"
}
Monitor ECS service events, task state changes, and CloudWatch alarms. Enable Container Insights for advanced metrics (CPU, memory per task, network) by opting in through the console or CLI:
aws ecs update-cluster-settings \
--cluster my-fargate-cluster \
--settings name=containerInsights,value=enabled
Best Practices
- Use Fargate for most workloads β It eliminates host management, simplifies security patching, and aligns cost with usage. Reserve EC2 launch type only for specialized requirements like GPU access or very high per-instance container density.
- Separate task IAM roles β Never use the execution role for application logic. Create distinct task roles with minimal permissions following least privilege.
- Version and pin task definitions in services β While you can use the family name (e.g.,
my-web-app) to always pull the latest active revision, pinning to a specific revision (e.g.,my-web-app:3) gives full control and rollback capability. - Enable health checks β Container-level health checks catch application failures that the process model misses. Combine with ALB health checks for comprehensive coverage.
- Store secrets securely β Use Secrets Manager or SSM Parameter Store and reference them via
secretsin the task definition instead of plaintext environment variables. - Infrastructure as Code β Define clusters, task definitions, services, and scaling policies in CloudFormation, Terraform, or CDK. This ensures reproducibility and audit trails.
- Monitor and alarm on service metrics β Set CloudWatch alarms on task count, service deployment state (
RUNNINGvsDEGRADED), and Container Insights metrics. Integrate with SNS or PagerDuty for incident response. - Plan for deployments β Use rolling updates with appropriate
minimumHealthyPercentandmaximumPercent. For zero-downtime releases, combine with CodeDeploy for blue/green or canary deployments.
Conclusion
Amazon ECS abstracts the complexity of container orchestration, letting you focus on your application code while AWS handles scheduling, scaling, and infrastructure. Through Fargate, you gain a serverless container platform that requires zero host management and integrates natively with a rich ecosystem of AWS services. By following the configuration steps outlinedβcreating a cluster, defining a secure task definition, registering it, running a service with load balancing, enabling auto scaling, and wiring in loggingβyou establish a production-grade container environment. Adhering to best practices around security, versioning, monitoring, and infrastructure as code ensures your container workloads remain resilient, cost-efficient, and easy to evolve. With this complete setup, you are ready to deploy, scale, and maintain containerized applications on ECS with confidence.