Skip to main content
This guide walks you through deploying a working ECS cluster with a Fargate service using the integrated root module. The cluster will run a sample frontend container with Fargate on-demand and Fargate Spot capacity providers.

Prerequisites

  • Terraform >= 1.5.7 installed (install guide)
  • AWS credentials configured in your environment (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or an IAM role via instance profile / SSO)
  • Existing VPC and subnets — you need subnet IDs to place your ECS tasks
Running this configuration creates real AWS resources that incur charges. Run terraform destroy when you no longer need the cluster.

Steps

1

Configure the AWS provider

Create a versions.tf file to pin the provider versions required by the module:
terraform {
  required_version = ">= 1.5.7"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 6.34"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}
2

Deploy an ECS cluster with a Fargate service

Create a main.tf with the integrated root module. This configuration creates a cluster with Fargate on-demand and Fargate Spot capacity providers, and deploys a service with two containers: a FluentBit sidecar for log forwarding and the application container.
module "ecs" {
  source = "terraform-aws-modules/ecs/aws"

  cluster_name = "ecs-integrated"

  cluster_configuration = {
    execute_command_configuration = {
      logging = "OVERRIDE"
      log_configuration = {
        cloud_watch_log_group_name = "/aws/ecs/aws-ec2"
      }
    }
  }

  # Cluster capacity providers
  cluster_capacity_providers = ["FARGATE", "FARGATE_SPOT"]
  default_capacity_provider_strategy = {
    FARGATE = {
      weight = 50
      base   = 20
    }
    FARGATE_SPOT = {
      weight = 50
    }
  }

  services = {
    ecsdemo-frontend = {
      cpu    = 1024
      memory = 4096

      # Container definition(s)
      container_definitions = {

        fluent-bit = {
          cpu       = 512
          memory    = 1024
          essential = true
          image     = "906394416424.dkr.ecr.us-west-2.amazonaws.com/aws-for-fluent-bit:stable"
          firelensConfiguration = {
            type = "fluentbit"
          }
          memoryReservation = 50
        }

        ecs-sample = {
          cpu       = 512
          memory    = 1024
          essential = true
          image     = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
          portMappings = [
            {
              name          = "ecs-sample"
              containerPort = 80
              protocol      = "tcp"
            }
          ]

          # Example image used requires access to write to root filesystem
          readonlyRootFilesystem = false

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

          enable_cloudwatch_logging = false
          logConfiguration = {
            logDriver = "awsfirelens"
            options = {
              Name                    = "firehose"
              region                  = "eu-west-1"
              delivery_stream         = "my-stream"
              log-driver-buffer-limit = "2097152"
            }
          }
          memoryReservation = 100
        }
      }

      service_connect_configuration = {
        namespace = "example"
        service = [{
          client_alias = {
            port     = 80
            dns_name = "ecs-sample"
          }
          port_name      = "ecs-sample"
          discovery_name = "ecs-sample"
        }]
      }

      load_balancer = {
        service = {
          target_group_arn = "arn:aws:elasticloadbalancing:eu-west-1:1234567890:targetgroup/bluegreentarget1/209a844cd01825a4"
          container_name   = "ecs-sample"
          container_port   = 80
        }
      }

      subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"]

      security_group_ingress_rules = {
        alb_3000 = {
          description                  = "Service port"
          from_port                    = 80
          ip_protocol                  = "tcp"
          referenced_security_group_id = "sg-12345678"
        }
      }
      security_group_egress_rules = {
        all = {
          ip_protocol = "-1"
          cidr_ipv4   = "0.0.0.0/0"
        }
      }
    }
  }

  tags = {
    Environment = "Development"
    Project     = "Example"
  }
}
Replace subnet_ids, target_group_arn, and referenced_security_group_id with real values from your AWS account before applying.
3

Apply and verify

Initialize and apply the configuration:
terraform init
terraform plan
terraform apply
After the apply completes, verify the cluster and service are running in the AWS console under ECS > Clusters, or use the AWS CLI:
aws ecs describe-clusters --clusters ecs-integrated
aws ecs list-services --cluster ecs-integrated

What was created

After a successful apply, you have:
ResourceDescription
ECS clusterNamed ecs-integrated with Fargate and Fargate Spot capacity providers
Capacity provider strategy50/50 weight split between FARGATE (with a base of 20) and FARGATE_SPOT
ECS serviceecsdemo-frontend service with a task definition and two container definitions
Task definitionRegisters the fluent-bit and ecs-sample container definitions with their CPU/memory allocations
CloudWatch log groupCreated by the module for ECS cluster execute command logging
Security groupCreated for the service with the ingress/egress rules you specified
Service IAM roleCreated automatically because the service uses a load balancer target group
Task execution IAM roleGrants ECS permission to pull images and write logs on behalf of your tasks
The service module always ignores changes to desired_count in Terraform to avoid conflicts with application autoscaling. To change the running task count after initial deployment, use the AWS console, CLI, or a separate null_resource with a local-exec provisioner.

Next steps

Fargate guide

Learn how to configure Fargate on-demand and Fargate Spot with mixed capacity provider strategies.

EC2 Auto Scaling

Attach an EC2 Auto Scaling Group as a capacity provider for full compute control.

Logging

Configure CloudWatch logging, FireLens, and FluentBit sidecars for your containers.

Autoscaling

Set up target tracking and scheduled scaling policies for your ECS services.

Build docs developers (and LLMs) love