All posts

Migrating an ECS service from your ALB to ECS Gateway without downtime

A step-by-step migration of an existing ECS service from a load balancer you manage to Amazon ECS Gateway, with the checks to run first, how to test and how to roll back.

You have a service behind a shared Application Load Balancer and want to move it to ECS Gateway. The good news: a gateway connector doesn’t touch your existing load balancer configuration. For a while the service is reachable both ways, so you can test the new path before anyone depends on it, and go back at any point until the very last step.

Check these first

Some of these can’t be fixed during the migration, so look at them before you start.

  • Network mode. Only awsvpc services can connect. A bridge service needs a new task definition first.
  • A named port. The connector refers to a port mapping by name. Older task definitions often have none, so add one, like "name": "web".
  • Same VPC and every Availability Zone. The gateway must sit in the service’s VPC and have subnets in every zone the service runs in. Tasks in a zone the gateway misses never pass their health check.
  • HTTPS on 443, hostname routing only. If you rely on path rules, redirects, ALB authentication or several services under one domain, the gateway can’t replace your load balancer yet.
  • Your domain. At the time of writing, a gateway only gives you a generated on.aws hostname. For a public service on your own domain you’ll need CloudFront in front, as the first part describes. Check the current docs, this may have changed.
  • The health check path. Set it when you add the connector. Changing it later replaces the target group, which ECS rejects for services with the ROLLING deployment strategy.
  • Public or private. If the service must never be reachable from the internet, filter the connector on PRIVATE. Otherwise it also connects to any public gateway that is later associated with the cluster.

The migration step by step

Step through it below, or press Play. The dots are requests, colored by the path they take.

Moving a service to a gateway

ClientsYour ALBrule + target groupwebservice tasks

Step 1 of 6: Before

Clients reach the service through your load balancer: your listener rule, your target group, your certificate.

Requests via your ALB Requests via the gateway Your test requests

1. Create and associate a gateway

The platform team creates the gateway with the infrastructure role and the right subnets, then associates it with the cluster. The first part has the full commands. Nothing is provisioned at this point and no traffic changes, so this step is safe to do well ahead of time.

2. Add the connector

aws ecs update-service \
  --cluster my-cluster \
  --service web \
  --gateway-connectors '[{
    "portName": "web",
    "filters": { "accessTypes": ["PUBLIC"] },
    "healthCheck": { "healthCheckPath": "/health" }
  }]'

This starts a normal deployment. ECS creates the gateway’s load balancer if needed, plus the rule, target group, certificate and hostname for this service. The service’s existing loadBalancers entry stays as it is, so new tasks register in both target groups.

3. Test the gateway URL

Get the hostname from the new service revision and wait until the connection is ACTIVE:

aws ecs describe-service-revisions \
  --service-revision-arns <revision-arn> \
  --query 'serviceRevisions[0].ecsManagedResources.gatewayConnections'

Then call it yourself. Check the response codes and latency, and that headers your app depends on, like Host or X-Forwarded-For, still look the way it expects:

curl -i https://<id>.ecs.us-east-1.on.aws/health

If the connection shows CREATE_FAILED, waiting won’t help. aws ecs describe-gateway-route has the reason.

4. Move the clients

How you switch depends on who calls the service:

  • Internal clients you control: change their configuration to the new hostname and deploy them.
  • A public service on your own domain: create a CloudFront distribution with the generated hostname as its origin and your certificate on it, test it, then point your DNS record at CloudFront.

Your load balancer still serves the service during this step. If something goes wrong, switch the clients back.

5. Remove the old load balancer

Watch your old target group’s RequestCount until it stays at zero for long enough to cover clients that cache DNS. Then deploy the service without it:

aws ecs update-service \
  --cluster my-cluster \
  --service web \
  --load-balancers '[]'

After that deployment, delete the listener rule, target group, certificate and DNS record from your infrastructure code. If the load balancer served only this service, delete it too.

If you manage the service in Terraform or CloudFormation, make the same changes there instead of on the command line, or the next apply puts the old load balancer back.

Rolling back

Until step 5, rolling back is cheap:

  • Before clients moved: remove the connector with --gateway-connectors '[]'. ECS deletes the gateway route and its resources after the deployment.
  • After clients moved: point them back at the old hostname. The old target group still has all tasks registered.

After step 5 you’d have to recreate your load balancer config, so give step 4 enough time.

Monitoring after the move

A gateway publishes no metrics of its own. You use the load balancer’s CloudWatch metrics with the TargetGroup dimension, which isolates your service on the shared load balancer. The three alarms worth having per service:

  • UnHealthyHostCount above zero,
  • TargetResponseTime above your usual level,
  • HTTPCode_Target_5XX_Count above zero.

The platform team should also watch HTTPCode_ELB_5XX_Count and RejectedConnectionCount on the shared load balancers. ECS tags every load balancer it creates with the gateway’s ARN, so alarms and dashboards can select them by tag as load balancers come and go.

Summary

Migrating to a gateway is low risk because both paths run side by side: add the connector, test the generated URL, move the clients, and remove your own load balancer last. Check the prerequisites first, especially the named port, the Availability Zones and how you’ll handle your own domain, because those are the ones that stop a migration halfway.