AWS ECS Fargate: Serverless Container Deployment
AWS ECS Fargate deployment provides the simplicity of serverless with the flexibility of containers. Unlike Lambda’s execution time limits and packaging constraints, Fargate runs any containerized application with no infrastructure management. Therefore, teams can focus on application logic while AWS handles server provisioning, patching, and scaling of the underlying compute. Each task runs on its own dedicated kernel and is isolated from other tenants, which sidesteps the noisy-neighbor and shared-AMI concerns that come with running containers on a self-managed EC2 cluster.
ECS Fargate eliminates the need to manage EC2 instances, AMIs, or cluster capacity. Moreover, you only pay for the vCPU and memory your tasks consume, with per-second billing. Consequently, Fargate is ideal for web applications, API servers, background workers, and any workload that runs longer than Lambda’s 15-minute limit or needs more than 10GB of memory. The trade-off, which the cost section below revisits, is that this convenience carries a per-vCPU premium over raw EC2, so the right choice depends heavily on how steady and predictable your load is.
Task Definition Best Practices
The task definition is the blueprint for your containers — specifying images, resource limits, environment variables, and logging configuration. A well-designed task definition ensures consistent deployments, proper resource allocation, and comprehensive observability. Furthermore, using Secrets Manager for sensitive values keeps credentials out of your task definitions.
{
"family": "order-service",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::123456789:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789:role/orderServiceTaskRole",
"containerDefinitions": [
{
"name": "order-service",
"image": "123456789.dkr.ecr.us-east-1.amazonaws.com/order-service:v1.5.2",
"essential": true,
"portMappings": [
{ "containerPort": 8080, "protocol": "tcp" }
],
"environment": [
{ "name": "SPRING_PROFILES_ACTIVE", "value": "production" },
{ "name": "SERVER_PORT", "value": "8080" }
],
"secrets": [
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:prod/db-password"
},
{
"name": "API_KEY",
"valueFrom": "arn:aws:ssm:us-east-1:123456789:parameter/prod/api-key"
}
],
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:8080/actuator/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/order-service",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
Two roles in that definition are frequently confused, and getting them wrong causes hard-to-diagnose failures. The executionRoleArn is used by the ECS agent itself — to pull the image from ECR and to fetch the secrets before the container starts — while the taskRoleArn grants permissions to the application code running inside the container, such as reading from S3 or writing to DynamoDB. A common mistake is granting Secrets Manager access to the task role instead of the execution role; the secret injection happens before your code runs, so the execution role is the one that needs it. Equally important, the only valid combinations of cpu and memory are fixed pairs, so 512 vCPU units must pair with 1024, 2048, 3072, or 4096 MiB, and an invalid pair is rejected outright at registration.
Networking and the awsvpc Mode
Every Fargate task uses the awsvpc network mode, which means each task receives its own elastic network interface and a private IP address directly inside your VPC. This is a meaningful improvement over bridge networking because security groups apply at the task level, giving you precise control over which services can talk to each other. As a result, you can lock down a database security group to accept traffic only from the order-service tasks, with no host-level port juggling.
For production, the recommended pattern is to run tasks in private subnets with no public IP, and place an Application Load Balancer in public subnets to receive inbound traffic. Outbound access to AWS APIs and ECR then flows through either a NAT gateway or, more cost-effectively at scale, VPC interface endpoints for ECR, Secrets Manager, and CloudWatch Logs. A frequently overlooked gotcha is that a task in a private subnet without a NAT gateway or the relevant VPC endpoints cannot pull its own image, and it fails to start with an opaque networking error that sends teams hunting in the wrong direction.
AWS ECS Fargate Deployment: Auto-Scaling Configuration
Fargate services auto-scale based on CloudWatch metrics — CPU utilization, memory usage, request count, or custom metrics. Target tracking policies are the simplest to configure and most effective for most workloads. Additionally, step scaling provides more granular control for bursty traffic patterns.
# CloudFormation: ECS Service with auto-scaling
OrderService:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref ECSCluster
TaskDefinition: !Ref OrderTaskDefinition
DesiredCount: 3
LaunchType: FARGATE
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DeploymentCircuitBreaker:
Enable: true
Rollback: true
NetworkConfiguration:
AwsvpcConfiguration:
Subnets: !Ref PrivateSubnets
SecurityGroups: [!Ref ServiceSG]
LoadBalancers:
- ContainerName: order-service
ContainerPort: 8080
TargetGroupArn: !Ref TargetGroup
# Auto-scaling
ScalableTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
ServiceNamespace: ecs
ResourceId: !Sub "service/${ECSCluster}/${OrderService.Name}"
ScalableDimension: ecs:service:DesiredCount
MinCapacity: 2
MaxCapacity: 20
CPUScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: cpu-tracking
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ScalableTarget
TargetTrackingScalingPolicyConfiguration:
TargetValue: 70.0
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
ScaleInCooldown: 300
ScaleOutCooldown: 60
The asymmetric cooldowns in that policy are deliberate and worth understanding. A short scale-out cooldown of 60 seconds lets the service react quickly when load spikes, while a longer scale-in cooldown of 300 seconds prevents flapping — repeatedly adding and removing tasks as the metric oscillates around the target. For request-driven services, teams often prefer the ALBRequestCountPerTarget metric over CPU, because request count is a more direct proxy for user-facing load and reacts before CPU saturation actually degrades latency. The key practical limit to remember is that Fargate task startup is measured in tens of seconds, not milliseconds, so scaling cannot absorb an instantaneous traffic cliff the way provisioned headroom can; sizing the minimum capacity for your baseline plus a buffer remains essential.
Blue-Green Deployments with CodeDeploy
ECS integrates with CodeDeploy for blue-green deployments — running the new version alongside the old one, shifting traffic gradually, and automatically rolling back if health checks fail. Furthermore, you can configure traffic shifting patterns from linear (10% every minute) to canary (10% first, then 90% after validation).
The deployment circuit breaker shown in the CloudFormation above is the simpler, built-in safety net: with rolling deployments it watches task health and automatically rolls back to the last known-good task set if new tasks fail to stabilize. CodeDeploy goes further by maintaining two complete target groups and letting you run automated validation against the green environment before any production traffic reaches it. A practical pattern is to pair canary traffic shifting with a CloudWatch alarm on error rate and p99 latency, so that a regression that passes basic health checks but degrades real performance still triggers an automatic rollback within minutes.
Cost Optimization
Right-size your tasks using Container Insights CPU and memory metrics. Additionally, use Fargate Spot for fault-tolerant workloads (up to 70% savings), and schedule non-critical services to scale down during off-hours. See the AWS ECS Fargate documentation for pricing details and optimization strategies.
- Use Container Insights to identify over-provisioned tasks
- Fargate Spot for batch processing and dev/staging environments
- Savings Plans for predictable production workloads (up to 50% off)
- Scale to zero for non-critical services during nights/weekends
Fargate Spot deserves a note of caution alongside its appeal. Spot tasks can be reclaimed with a two-minute warning delivered as a SIGTERM, so they are excellent for stateless workers and queue consumers that can checkpoint and exit cleanly, but inappropriate for a stateful process that cannot tolerate abrupt termination. A robust pattern is to trap that termination signal, drain in-flight work, and let ECS replace the task. For steady production traffic, Compute Savings Plans typically deliver the better economics, while Spot shines for the spiky, interruption-tolerant tail of the workload.
When NOT to Use Fargate
Fargate is not the universal answer, and choosing it reflexively can cost more than it saves. For large, steady-state fleets running around the clock, EC2-backed ECS or EKS with Karpenter and Reserved Instances is usually cheaper, because you pay for the instance rather than the per-task premium and can pack many containers onto each host. Likewise, workloads that need GPUs, specialized instance types, privileged kernel access, or host-level daemons fit poorly within Fargate’s deliberately constrained, fully managed model.
There are also feature edges to weigh: Fargate’s ephemeral storage is capped, persistent state must live in external services such as EFS, RDS, or DynamoDB, and very latency-sensitive workloads may notice the cold-start time of launching a fresh task. The honest rule of thumb is that Fargate maximizes operational simplicity per task, while self-managed compute maximizes cost efficiency and flexibility at scale. Many mature platforms end up running both, using Fargate for spiky or low-volume services and a managed node pool for the dense, predictable core. For a related discussion of that broader trade-off, see our comparison of serverless versus containers on cost and performance.
In conclusion, AWS ECS Fargate deployment provides the ideal balance between serverless simplicity and container flexibility for a wide range of workloads. With proper task definitions, correct role separation, sound VPC networking, target-tracking auto-scaling, and safe blue-green or circuit-breaker deployments, you can run production services with confidence. Start with a simple service, add auto-scaling, layer in advanced deployment strategies, and continuously revisit cost as your traffic patterns mature — the right answer often shifts as your workload grows.