Skip to main content
ECS Managed Instances is a capacity provider type where AWS provisions and manages EC2 instances on your behalf. Unlike the EC2 Auto Scaling capacity provider, you do not create or operate an Auto Scaling Group — instead, you describe the compute requirements and AWS handles the rest.

How it differs from EC2 Auto Scaling

EC2 Auto ScalingECS Managed Instances
Infrastructure managementYou create and manage the ASGAWS provisions and manages instances
Instance selectionFixed instance type per ASGFlexible instance requirements (CPU, memory, family)
IAM setupInstance profile on ASG launch templateInfrastructure role + node role created by module
Security groupManaged externallyCreated by cluster module, configured via inputs
requires_compatibilities["EC2"]["MANAGED_INSTANCES"]
ECS Managed Instances uses MANAGED_INSTANCES in requires_compatibilities and EC2 as the launch_type. Both must be set when deploying services onto this capacity provider.

IAM role requirements

The cluster module creates two IAM roles automatically when using a managed instances capacity provider: Infrastructure role (create_infrastructure_iam_role = true by default) Used by ECS to provision, manage, and terminate EC2 instances on your behalf. Node role (create_node_iam_instance_profile = true by default) Attached to the EC2 instances as an instance profile, providing permissions for the container agent running on the instances. You can bring your own roles by setting create_infrastructure_iam_role = false and providing infrastructure_iam_role_arn, or by setting create_node_iam_instance_profile = false and providing the ARN via ec2_instance_profile_arn inside the instance_launch_template.

Configuring instance requirements

Instead of specifying a single instance type, you define requirements and ECS selects matching instances. The instance_requirements block mirrors the EC2 Fleet attribute-based instance selection API.
module "ecs_cluster" {
  source = "terraform-aws-modules/ecs/aws//modules/cluster"

  name = local.name

  capacity_providers = {
    mi-example = {
      managed_instances_provider = {
        instance_launch_template = {
          instance_requirements = {
            instance_generations = ["current"]
            cpu_manufacturers    = ["intel", "amd"]

            memory_mib = {
              max = 8192
              min = 1024
            }

            vcpu_count = {
              max = 4
              min = 1
            }
          }

          network_configuration = {
            subnets = module.vpc.private_subnets
          }

          storage_configuration = {
            storage_size_gib = 30
          }
        }
      }
    }
  }

  tags = local.tags
}

Key instance requirement fields

instance_requirements = {
  vcpu_count = {
    min = 1
    max = 4
  }
  memory_mib = {
    min = 1024
    max = 8192
  }
}
Set min and max bounds on vCPU count and memory in MiB. ECS selects instances within these ranges.
instance_requirements = {
  instance_generations = ["current"]
  cpu_manufacturers    = ["intel", "amd"]
}
Use instance_generations = ["current"] to exclude previous-generation instance types. cpu_manufacturers accepts intel, amd, and amazon-web-services (for Graviton).
instance_requirements = {
  allowed_instance_types = ["t3.medium", "t3.large", "m5.large"]
}
Use allowed_instance_types to constrain to a specific list instead of using attribute-based selection.

Network configuration

Specify which subnets the managed instances should be launched into:
instance_launch_template = {
  network_configuration = {
    subnets = module.vpc.private_subnets
  }
  # ...
}

Storage configuration

Set the root volume size for the managed instances:
instance_launch_template = {
  storage_configuration = {
    storage_size_gib = 30
  }
  # ...
}

Security group setup

The cluster module creates a security group for the managed instances when create_security_group = true (the default). Configure ingress and egress rules via security_group_ingress_rules and security_group_egress_rules:
module "ecs_cluster" {
  source = "terraform-aws-modules/ecs/aws//modules/cluster"

  # ... other config

  # Managed instances security group
  vpc_id = module.vpc.vpc_id
  security_group_ingress_rules = {
    alb_http = {
      from_port                    = local.container_port
      description                  = "Service port"
      referenced_security_group_id = module.alb.security_group_id
    }
  }
  security_group_egress_rules = {
    all = {
      cidr_ipv4   = "0.0.0.0/0"
      ip_protocol = "-1"
    }
  }

  tags = local.tags
}
You must provide vpc_id so the module can create the security group in the correct VPC.

Deploying a service onto managed instances

Services targeting a managed instances capacity provider set requires_compatibilities = ["MANAGED_INSTANCES"] and launch_type = "EC2". Reference the capacity provider by name from the cluster outputs:
module "ecs_service" {
  source = "terraform-aws-modules/ecs/aws//modules/service"

  name        = local.name
  cluster_arn = module.ecs_cluster.arn

  requires_compatibilities = ["MANAGED_INSTANCES"]
  launch_type              = "EC2"

  container_definitions = {
    (local.container_name) = {
      image = "public.ecr.aws/docker/library/httpd:latest"

      essential  = true
      entrypoint = ["sh", "-c"]
      command    = ["/bin/sh -c \"echo '<html>...</html>' > /usr/local/apache2/htdocs/index.html && httpd-foreground\""]

      cpu    = 256
      memory = 512

      readonlyRootFilesystem = false

      portMappings = [
        {
          name          = local.container_name
          containerPort = local.container_port
          hostPort      = local.container_port
          protocol      = "tcp"
        }
      ]
    }
  }

  capacity_provider_strategy = {
    mi-example = {
      capacity_provider = module.ecs_cluster.capacity_providers["mi-example"].name
    }
  }

  load_balancer = {
    service = {
      target_group_arn = module.alb.target_groups["ex-ecs"].arn
      container_name   = local.container_name
      container_port   = local.container_port
    }
  }

  subnet_ids = module.vpc.private_subnets
  security_group_ingress_rules = {
    alb_http = {
      from_port                    = local.container_port
      description                  = "Service port"
      referenced_security_group_id = module.alb.security_group_id
    }
  }

  tags = local.tags
}

Complete example

The following shows the full cluster + service configuration from the managed-instances example:
module "ecs_cluster" {
  source = "terraform-aws-modules/ecs/aws//modules/cluster"

  name = local.name

  capacity_providers = {
    mi-example = {
      managed_instances_provider = {
        instance_launch_template = {
          instance_requirements = {
            instance_generations = ["current"]
            cpu_manufacturers    = ["intel", "amd"]

            memory_mib = {
              max = 8192
              min = 1024
            }

            vcpu_count = {
              max = 4
              min = 1
            }
          }

          network_configuration = {
            subnets = module.vpc.private_subnets
          }

          storage_configuration = {
            storage_size_gib = 30
          }
        }
      }
    }
  }

  vpc_id = module.vpc.vpc_id
  security_group_ingress_rules = {
    alb_http = {
      from_port                    = local.container_port
      description                  = "Service port"
      referenced_security_group_id = module.alb.security_group_id
    }
  }
  security_group_egress_rules = {
    all = {
      cidr_ipv4   = "0.0.0.0/0"
      ip_protocol = "-1"
    }
  }

  tags = local.tags
}

Build docs developers (and LLMs) love