Skip to main content
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:
InputDefaultDescription
autoscaling_min_capacity1Minimum number of tasks.
autoscaling_max_capacity10Maximum number of tasks.
autoscaling_policiesCPU + memory target trackingDefault 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.
FieldRequiredDescription
min_capacityYesMinimum number of tasks during this action.
max_capacityYesMaximum number of tasks during this action.
scheduleYesCron, rate, or at expression.
start_timeNoISO 8601 timestamp when the action starts being active.
end_timeNoISO 8601 timestamp when the action stops being active.
timezoneNoIANA 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
  }
}
FieldDefaultDescription
dynamic_scaling_in_suspendedfalseSuspend scale-in actions from dynamic (target tracking) policies.
dynamic_scaling_out_suspendedfalseSuspend scale-out actions from dynamic policies.
scheduled_scaling_suspendedfalseSuspend 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
  }
}

Build docs developers (and LLMs) love