Application Auto Scaling adjusts the number of running ECS tasks based on CloudWatch metrics or a schedule. The service module enables autoscaling by default and creates an aws_appautoscaling_target, aws_appautoscaling_policy, and optionally aws_appautoscaling_scheduled_action resources.
The service module always ignores desired_count. Once autoscaling is active, the number of tasks is controlled by Application Auto Scaling, not Terraform. See Managing desired count externally for the workaround when autoscaling is disabled.
Default behavior
Autoscaling is enabled by default (enable_autoscaling = true) with the following defaults:
| Input | Default | Description |
|---|
autoscaling_min_capacity | 1 | Minimum number of tasks. |
autoscaling_max_capacity | 10 | Maximum number of tasks. |
autoscaling_policies | CPU + memory target tracking | Default policies applied automatically. |
The two default target tracking policies are:
ECSServiceAverageCPUUtilization — scales out when average CPU across tasks exceeds the target.
ECSServiceAverageMemoryUtilization — scales out when average memory across tasks exceeds the target.
Both policies scale in when utilization drops below the target.
Custom autoscaling policies
Override or extend the default policies by providing a autoscaling_policies map. Each key names the policy:
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# ... other config
autoscaling_min_capacity = 2
autoscaling_max_capacity = 20
autoscaling_policies = {
cpu = {
policy_type = "TargetTrackingScaling"
target_tracking_scaling_policy_configuration = {
predefined_metric_specification = {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
target_value = 70
}
}
memory = {
policy_type = "TargetTrackingScaling"
target_tracking_scaling_policy_configuration = {
predefined_metric_specification = {
predefined_metric_type = "ECSServiceAverageMemoryUtilization"
}
target_value = 80
}
}
}
}
Start with target values around 70% for CPU and 80% for memory. This leaves enough headroom for traffic spikes before new tasks become healthy.
Scheduled scaling actions
Use autoscaling_scheduled_actions to pre-scale the service before predictable traffic changes (for example, before business hours):
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# ... other config
autoscaling_scheduled_actions = {
scale-up = {
min_capacity = 5
max_capacity = 20
schedule = "cron(0 8 * * ? *)"
timezone = "Europe/London"
}
scale-down = {
min_capacity = 1
max_capacity = 5
schedule = "cron(0 20 * * ? *)"
timezone = "Europe/London"
}
}
}
The schedule field accepts:
cron(...) — a cron expression in UTC or the specified timezone.
rate(...) — a rate expression such as rate(5 minutes).
at(...) — a one-time action at a specific UTC timestamp.
| Field | Required | Description |
|---|
min_capacity | Yes | Minimum number of tasks during this action. |
max_capacity | Yes | Maximum number of tasks during this action. |
schedule | Yes | Cron, rate, or at expression. |
start_time | No | ISO 8601 timestamp when the action starts being active. |
end_time | No | ISO 8601 timestamp when the action stops being active. |
timezone | No | IANA timezone for the cron expression. Defaults to UTC. |
Suspending scaling activities
You can temporarily suspend specific scaling processes without removing the policies:
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# ... other config
autoscaling_suspended_state = {
dynamic_scaling_in_suspended = false
dynamic_scaling_out_suspended = false
scheduled_scaling_suspended = true
}
}
| Field | Default | Description |
|---|
dynamic_scaling_in_suspended | false | Suspend scale-in actions from dynamic (target tracking) policies. |
dynamic_scaling_out_suspended | false | Suspend scale-out actions from dynamic policies. |
scheduled_scaling_suspended | false | Suspend all scheduled scaling actions. |
Disabling autoscaling
Set enable_autoscaling = false to disable autoscaling entirely. No aws_appautoscaling_target or policies will be created:
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# ... other config
enable_autoscaling = false
desired_count = 3
}
When autoscaling is disabled, the desired_count you set in Terraform controls the initial number of tasks. However, because the service module always ignores desired_count in subsequent applies (to avoid conflicts with autoscaling), any changes to desired_count in Terraform will not be applied to the running service after the first creation.
Managing desired count externally
Because desired_count is always ignored by the module’s lifecycle rules, changes to the number of running tasks must be made outside of Terraform when autoscaling is disabled. One approach is a null_resource that calls the AWS CLI:
resource "null_resource" "update_desired_count" {
triggers = {
# Changes to this value will trigger the API call execution below
desired_count = 3
}
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
# Note: this requires the awscli to be installed locally where Terraform is executed
command = <<-EOT
aws ecs update-service \
--cluster ${module.ecs.cluster_name} \
--service ${module.ecs_service.name} \
--desired-count ${null_resource.update_desired_count.triggers.desired_count}
EOT
}
}
Change the desired_count value in the triggers block to trigger the CLI call on the next terraform apply. This pattern is equivalent to the EKS desired size hack.
This workaround requires the AWS CLI to be installed on the machine running Terraform. It also requires appropriate IAM permissions to call ecs:UpdateService.
Task set scale
When using an external deployment controller with task sets, the equivalent of desired_count is the task set scale attribute. The service module ignores scale in the same way it ignores desired_count. To update scale externally:
resource "null_resource" "update_scale" {
triggers = {
# Changes to this value will trigger the API call execution below
scale = "value=50,unit=PERCENT"
}
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
# Note: this requires the awscli to be installed locally where Terraform is executed
command = <<-EOT
aws ecs update-task-set \
--cluster ${module.ecs.cluster_name} \
--service ${module.ecs_service.name} \
--task-set ${module.ecs_service.task_set_arn} \
--scale ${null_resource.update_scale.triggers.scale}
EOT
}
}