Skip to main content
The Express Service module deploys an aws_ecs_express_gateway_service resource — a simplified ECS deployment that automatically provisions a public URL for the service without requiring a load balancer.

What is created

  • ECS Express Gateway Service with a public *.ecs.{region}.on.aws URL
  • IAM roles: execution role, task role, infrastructure role
  • Security group
  • CloudWatch log group
  • VPC with private/public subnets
The Express Service module does not require an ECS cluster or load balancer. It is designed for simplified deployments where you want ECS to manage the full service lifecycle and endpoint.

Code

main.tf
provider "aws" {
  region = local.region
}

resource "random_string" "random" {
  length  = 6
  special = false
}

locals {
  region = "eu-west-1"
  # Service names are held indefinitely by ECS, so a random suffix prevents conflicts
  name   = "ex-${basename(path.cwd)}-${random_string.random.result}"

  vpc_cidr       = "10.0.0.0/16"
  azs            = slice(data.aws_availability_zones.available.names, 0, 3)
  container_port = 3000

  tags = {
    Name       = local.name
    Example    = local.name
    Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs"
  }
}

module "ecs_express_service" {
  source = "terraform-aws-modules/ecs/aws//modules/express-service"

  name = local.name

  cpu    = 1024
  memory = 4096

  network_configuration = {
    subnets = module.vpc.private_subnets
  }

  primary_container = {
    container_port = local.container_port
    image          = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
  }

  scaling_target = {
    auto_scaling_metric       = "AVERAGE_CPU"
    auto_scaling_target_value = "80"
    max_task_count            = 3
    min_task_count            = 1
  }

  vpc_id = module.vpc.vpc_id
  security_group_egress_rules = {
    all = {
      ip_protocol = "-1"
      cidr_ipv4   = "0.0.0.0/0"
    }
  }

  tags = local.tags
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 6.0"

  name = local.name
  cidr = local.vpc_cidr

  azs             = local.azs
  private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
  public_subnets  = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)]

  enable_nat_gateway = true
  single_nat_gateway = true

  tags = local.tags
}

Service URL output

After apply, retrieve the public service URL:
outputs.tf
output "service_url" {
  value = module.ecs_express_service.service_url
}
# Output: https://ex-express-service-abc123.ecs.eu-west-1.on.aws/

Key highlights

  • No cluster or ALB required: Express Service automatically provisions a public endpoint.
  • Random name suffix: ECS holds service names indefinitely after deletion. The random suffix prevents naming conflicts when recreating the service during development.
  • scaling_target: Built-in CPU or memory-based autoscaling without configuring Application Auto Scaling separately.
  • primary_container: A simplified container config — no need to write a full container definition block.

Express Service Inputs

Full reference for all Express Service parameters.

Service Module

Full-featured service module for more complex deployments.

Build docs developers (and LLMs) love