Skip to main content
Fargate is the default and recommended compute option for this module. With Fargate, AWS manages the underlying EC2 infrastructure so you only define CPU and memory requirements at the task level. Fargate Spot runs tasks on spare AWS capacity at a significant cost reduction but with the possibility of interruption.

Fargate (on-demand)

Guaranteed capacity. Best for production workloads, latency-sensitive services, and tasks that cannot tolerate interruption.

Fargate Spot

Up to 70% cheaper. Best for batch jobs, non-critical background processing, and fault-tolerant workloads that can restart on interruption.
You cannot mix EC2-based capacity providers with Fargate capacity providers on the same cluster. Choose one compute family per cluster.

Creating a cluster with Fargate capacity providers

1

Define the cluster module

Use the cluster sub-module and specify which Fargate capacity providers to enable. Both FARGATE and FARGATE_SPOT are AWS-managed names — you reference them directly rather than creating them.
module "ecs_cluster" {
  source = "terraform-aws-modules/ecs/aws//modules/cluster"

  name = local.name

  # Capacity provider
  cluster_capacity_providers = ["FARGATE", "FARGATE_SPOT"]
  default_capacity_provider_strategy = {
    FARGATE = {
      weight = 50
      base   = 20
    }
    FARGATE_SPOT = {
      weight = 50
    }
  }

  tags = local.tags
}
2

Understand weight and base

The default_capacity_provider_strategy controls how tasks are distributed:
  • base — the minimum number of tasks to always place on this provider before weight applies. In the example above, the first 20 tasks always go to FARGATE.
  • weight — a relative proportion used to distribute tasks beyond the base. Equal weights (50/50) split tasks evenly. A 60/40 split would send 60% to one provider and 40% to the other.
Set a base on FARGATE to ensure at least some on-demand tasks are always running, then use FARGATE_SPOT for the remainder to reduce costs.
3

Deploy a service onto the cluster

Point the service module at your cluster and define container definitions. The service defaults to FARGATE launch type and awsvpc networking.
module "ecs_service" {
  source = "terraform-aws-modules/ecs/aws//modules/service"

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

  cpu    = 1024
  memory = 4096

  container_definitions = {
    (local.container_name) = {
      cpu       = 512
      memory    = 1024
      essential = true
      image     = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
      portMappings = [
        {
          name          = local.container_name
          containerPort = local.container_port
          hostPort      = local.container_port
          protocol      = "tcp"
        }
      ]

      readonlyRootFilesystem = false
    }
  }

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

  tags = local.tags
}

Networking with awsvpc

Fargate requires awsvpc network mode, which is the default for this module. Each task gets its own elastic network interface (ENI) with a private IP from your VPC subnets. You must provide subnet_ids for the service. The module creates a security group automatically and lets you define ingress and egress rules:
module "ecs_service" {
  source = "terraform-aws-modules/ecs/aws//modules/service"

  # ... other config

  subnet_ids = module.vpc.private_subnets

  security_group_ingress_rules = {
    alb_3000 = {
      description                  = "Service port"
      from_port                    = local.container_port
      ip_protocol                  = "tcp"
      referenced_security_group_id = module.alb.security_group_id
    }
  }
  security_group_egress_rules = {
    all = {
      ip_protocol = "-1"
      cidr_ipv4   = "0.0.0.0/0"
    }
  }
}
Place tasks in private subnets and expose them through an Application Load Balancer in public subnets. Never assign public IPs to tasks unless required — set assign_public_ip = true only when tasks have no NAT gateway access.

Adding a FireLens sidecar

The Fargate example uses a FluentBit sidecar for log forwarding. Add the fluent-bit container definition alongside your application container and configure awsfirelens as the log driver:
container_definitions = {

  fluent-bit = {
    cpu       = 512
    memory    = 1024
    essential = true
    image     = nonsensitive(data.aws_ssm_parameter.fluentbit.value)
    firelensConfiguration = {
      type = "fluentbit"
    }
    memoryReservation = 50
    user              = "0"
  }

  (local.container_name) = {
    cpu       = 512
    memory    = 1024
    essential = true
    image     = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
    portMappings = [
      {
        name          = local.container_name
        containerPort = local.container_port
        hostPort      = local.container_port
        protocol      = "tcp"
      }
    ]

    readonlyRootFilesystem = false

    dependsOn = [{
      containerName = "fluent-bit"
      condition     = "START"
    }]

    enable_cloudwatch_logging = false
    logConfiguration = {
      logDriver = "awsfirelens"
      options = {
        Name                    = "stdout"
        log-driver-buffer-limit = "2097152"
      }
    }

    memoryReservation = 100
  }
}

data "aws_ssm_parameter" "fluentbit" {
  name = "/aws/service/aws-for-fluent-bit/stable"
}
See the Logging guide for all supported logging scenarios.

Blue/green deployments

The Fargate example enables blue/green deployments using the ECS-native deployment configuration:
module "ecs_service" {
  source = "terraform-aws-modules/ecs/aws//modules/service"

  # ... other config

  deployment_configuration = {
    strategy             = "BLUE_GREEN"
    bake_time_in_minutes = 2
  }

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

      advanced_configuration = {
        alternate_target_group_arn = module.alb.target_groups["ex-ecs-alternate"].arn
        production_listener_rule   = module.alb.listener_rules["ex-http/production"].arn
        test_listener_rule         = module.alb.listener_rules["ex-http/test"].arn
      }
    }
  }
}
See the Blue/Green deployments guide for a complete walkthrough including CodeDeploy-based deployments.

Build docs developers (and LLMs) love