Automated ECS Cluster Scaling for Cost Optimization
How to automatically shut down and start up services and EC2 instances in an ECS cluster to reduce costs outside of business hours
Cloud cost management is crucial, especially for development or testing environments that don't need to run 24/7. The following Terraform code allows you to automate the process of shutting down an AWS ECS cluster and its services at night and turning them back on in the morning. This way, you can significantly lower costs by paying only for the resources you actually use.
The code below defines schedules for the Auto Scaling group of EC2 instances and for the individual ECS services.
# Define a local service map to easily manage them all at once.
# Update this map to point to your ECS service modules.
locals {
services = {
# Replace the names and references with your service modules
frontend = module.service_my_app_frontend
backend = module.service_my_app_backend
worker = module.service_my_app_worker
}
}
################################################################################
# SCHEDULED SHUTDOWN AND STARTUP OF THE CLUSTER (EC2 INSTANCES)
################################################################################
# This action shuts down EC2 instances (scales to 0) at 22:05.
resource "aws_autoscaling_schedule" "scale_down_ec2_cluster" {
scheduled_action_name = "scale-down-ecs-cluster-nightly"
# Change the name below to your cluster's autoscaling group name.
autoscaling_group_name = module.cluster.autoscaling_group.name
recurrence = "5 20 * * *" # Cron in UTC (e.g., 22:05 CEST is 20:05 UTC)
min_size = 0
max_size = 0
desired_capacity = 0
}
# This action turns on EC2 instances (scales to 1) at 05:55.
resource "aws_autoscaling_schedule" "scale_up_ec2_cluster" {
scheduled_action_name = "scale-up-ecs-cluster-daily"
# Change the name below to your cluster's autoscaling group name.
autoscaling_group_name = module.cluster.autoscaling_group.name
recurrence = "55 3 * * *" # Cron in UTC (e.g., 05:55 CEST is 03:55 UTC)
min_size = 1 # Set the target values for your environment.
max_size = 3
desired_capacity = 1
}
################################################################################
# SCHEDULED SHUTDOWN AND STARTUP OF ALL ECS SERVICES
################################################################################
# Register each service as a target for application auto-scaling.
resource "aws_appautoscaling_target" "ecs_service_targets" {
for_each = local.services
max_capacity = 1
min_capacity = 1
# Change 'module.cluster.name' if your cluster name comes from a different source.
resource_id = "service/${module.cluster.name}/${each.value.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
# Action to shut down all services (scaling to 0) at 22:00.
resource "aws_appautoscaling_scheduled_action" "scale_down_ecs_services" {
for_each = local.services
name = "${each.key}-scale-down-nightly"
service_namespace = aws_appautoscaling_target.ecs_service_targets[each.key].service_namespace
resource_id = aws_appautoscaling_target.ecs_service_targets[each.key].resource_id
scalable_dimension = aws_appautoscaling_target.ecs_service_targets[each.key].scalable_dimension
schedule = "cron(0 20 * * ? *)" # Cron in UTC (e.g., 22:00 CEST is 20:00 UTC)
scalable_target_action {
min_capacity = 0
max_capacity = 0
}
}
# Action to turn on all services (scaling to 1) at 06:00.
resource "aws_appautoscaling_scheduled_action" "scale_up_ecs_services" {
for_each = local.services
name = "${each.key}-scale-up-daily"
service_namespace = aws_appautoscaling_target.ecs_service_targets[each.key].service_namespace
resource_id = aws_appautoscaling_target.ecs_service_targets[each.key].resource_id
scalable_dimension = aws_appautoscaling_target.ecs_service_targets[each.key].scalable_dimension
schedule = "cron(0 4 * * ? *)" # Cron in UTC (e.g., 06:00 CEST is 04:00 UTC)
scalable_target_action {
min_capacity = 1
max_capacity = 1
}
}Remember the Time Zone
All schedules (recurrence and schedule) in AWS are defined in the UTC time zone. Make sure you convert the hours to your local time zone, accounting for daylight saving time. The example shows conversions for Central European Summer Time (CEST = UTC+2).