Deploy a web service with ECS Express Mode

Run a container as a public or internal HTTPS web service with one ECS Express Mode call — what it creates (Fargate service, shared ALB with ACM certificate, target group, security groups, auto scaling, logs, alarm), the three inputs, the defaults to override, what can never be changed afterwards, and how it replaces App Runner.

markdownguidev4342J-XT4Estage: buildtrust: officialservice: ecsinterface: cliECS Express Modecreate-express-gateway-serviceCreateExpressGatewayServiceApp Runner replacementinfrastructure roleexpress gateway serviceshared ALB 25 servicescanary deployment ECSExpress Mode defaultsmigrate from App Runner

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

Give Express Mode a container image, a task execution role and an infrastructure role, and it builds the whole service:

  • a Fargate service with canary deployments
  • an HTTPS Application Load Balancer with an ACM certificate
  • target group, security groups and auto scaling
  • logs and a deployment alarm

Decide the service name, cluster, subnets and tags before the call: they can't be changed later. It is AWS's recommended path for workloads that would have used App Runner, which is closed to new customers.

Inputs

InputNotes
Container imageAn ECR (or other registry) image. App Runner source-code services need a build step that produces an image first
Task execution role ARNAs for any Fargate task: image pull, logs, secrets (reference/ecs/roles-and-networking.md)
Infrastructure role ARNLets ECS create and manage the ALB, target group, security groups and scaling on your behalf. Its required policy isn't on the fetched page (UNVERIFIED)
Task role ARN (optional)Your application's AWS permissions
aws ecs create-express-gateway-service \
  --primary-container '{"image":"<account>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>","containerPort":8080}' \
  --execution-role-arn <task execution role ARN> \
  --infrastructure-role-arn <infrastructure role ARN> \
  --cpu 512 --memory 1024 \
  --scaling-target '{"minTaskCount":2}'

The flag names were checked against the CreateExpressGatewayService API model in botocore 1.43.115. The image goes inside --primary-container (image, containerPort, plus optional command, environment, secrets, awsLogsConfiguration, repositoryCredentials). Only --infrastructure-role-arn is required by the model. --cpu and --memory are strings. --scaling-target also takes maxTaskCount, autoScalingMetric and autoScalingTargetValue. --network-configuration, --health-check-path and --task-role-arn are the other overrides.

What it creates

  • The default cluster (if missing), with Fargate capacity providers, a task definition, and a REPLICA service using canary deployments.
  • An ALB with an HTTPS:443 listener, host-header rules and an ACM certificate. One ALB is shared by up to 25 Express services in the same VPC.
  • A target group: HTTP, ip targets, health check path / every 30 s.
  • Security groups for the service and the load balancer.
  • Auto scaling: target tracking on CPU at 60%, between 1 and 20 tasks.
  • A CloudWatch log group, an alarm for faulty deployments, and service-linked roles.

Defaults: 1 vCPU (1024), 2048 MB, container port 80, desired count 1, X86_64 Linux on Fargate.

Decide before you create

SettingChangeable later?
Service name, cluster, tagsNo, only at creation
Deployment strategy (canary)No
Load balancer configurationNo
SubnetsPublic subnets give an internet-facing ALB; private subnets an internal one. The first Express service in a VPC fixes the ALB's subnets, so later services must use the same AZs
Default container nameDon't rename it: later Express updates can break
CPU, memory, scaling, security groupsOverridable at creation (--cpu, --memory, --scaling-target, --network-configuration)

Common mismatches

  • The app listens on 8080 but the default port is 80. Health checks fail, and the deployment alarm rolls back. Set the container port, or make the app listen on 80 (port override flag UNVERIFIED).
  • The health check is on /. An app that redirects or requires auth on / fails it. Serve a cheap 200 there, or change the target group's health path (reference/elb/health-checks-and-timeouts.md).
  • A private-subnet service has no route for image pulls. The usual Fargate rules apply (patterns/ecs-to-ecr.md).

Migrating from App Runner (AWS's procedure)

  1. Build and push an image (if App Runner built from source).
  2. Create the Express service from that image.
  3. Add your custom domain to the ALB's host-header rule and certificate.
  4. Shift traffic with Route 53 weighted records: 90/10 → 75/25 → 50/50 → 25/75 → 100/0.
  5. Keep App Runner for 24–48 hours for rollback, then aws apprunner delete-service --service-arn <arn>.

Update and delete procedures for Express services weren't on the fetched page. The result is a normal ECS service, so guides/ecs/run-fargate-task.md and reference/ecs/errors.md apply to troubleshooting.

Read this document as markdown — the same bytes an agent receives.