A running task is useless if nothing can reach it. Two mechanisms connect ECS services to callers: an Application Load Balancer for external, internet-facing traffic, and AWS Cloud Map for internal service-to-service discovery inside the VPC.
Both attach at the service level and both keep themselves in sync with the fleet as tasks come and go. This page wires up each one via the SDK.
For external traffic you attach the service to an ALB target group. ECS registers each task as a target and deregisters it when the task stops. Because Fargate uses awsvpc networking, the target group must use the ip target type.
The containerName and containerPort must match a portMapping in the task definition. ECS now registers every task's private IP into the target group, and the ALB load-balances across them.
For internal calls between services you do not want a load balancer per service - you want DNS. Cloud Map gives each service a name like orders.internal that resolves to its healthy task IPs. You create the namespace and Cloud Map service with the servicediscovery client, then reference its ARN in the ECS service's serviceRegistries.
# --- Python (boto3) ---import boto3sd = boto3.client("servicediscovery", region_name="us-east-1")# 1. A private DNS namespace, once per VPC (this is async; poll the operation).op = sd.create_private_dns_namespace(Name="internal", Vpc="vpc-0abc")# 2. A discovery service that produces A records for task IPs.disc = sd.create_service( Name="orders", NamespaceId="ns-0abc", # from the namespace once created DnsConfig={"DnsRecords": [{"Type": "A", "TTL": 15}]}, HealthCheckCustomConfig={"FailureThreshold": 1},)registry_arn = disc["Service"]["Arn"]# 3. Wire the ECS service to it.ecs = boto3.client("ecs", region_name="us-east-1")ecs.create_service( cluster="prod", serviceName="orders", taskDefinition="orders", desiredCount=2, launchType="FARGATE", serviceRegistries=[{"registryArn": registry_arn}], networkConfiguration={ "awsvpcConfiguration": { "subnets": ["subnet-0a"], "securityGroups": ["sg-0orders"], } },)
// --- TypeScript (AWS SDK v3) ---import { ServiceDiscoveryClient, CreatePrivateDnsNamespaceCommand, CreateServiceCommand as CreateDiscoveryServiceCommand } from "@aws-sdk/client-servicediscovery";import { ECSClient, CreateServiceCommand } from "@aws-sdk/client-ecs";const sd = new ServiceDiscoveryClient({ region: "us-east-1" });// 1. A private DNS namespace, once per VPC (async; poll the operation).await sd.send(new CreatePrivateDnsNamespaceCommand({ Name: "internal", Vpc: "vpc-0abc" }));// 2. A discovery service that produces A records for task IPs.const disc = await sd.send(new CreateDiscoveryServiceCommand({ Name: "orders", NamespaceId: "ns-0abc", // from the namespace once created DnsConfig: { DnsRecords: [{ Type: "A", TTL: 15 }] }, HealthCheckCustomConfig: { FailureThreshold: 1 },}));const registryArn = disc.Service?.Arn;// 3. Wire the ECS service to it.const ecs = new ECSClient({ region: "us-east-1" });await ecs.send(new CreateServiceCommand({ cluster: "prod", serviceName: "orders", taskDefinition: "orders", desiredCount: 2, launchType: "FARGATE", serviceRegistries: [{ registryArn }], networkConfiguration: { awsvpcConfiguration: { subnets: ["subnet-0a"], securityGroups: ["sg-0orders"], }, },}));
Now other services in the VPC resolve orders.internal to the current healthy task IPs. As ECS starts and stops orders tasks, it registers and deregisters their IPs in Cloud Map, and the DNS answer follows automatically.
On the EC2 launch type with bridge networking, ALB targets are instances plus a host port. Fargate uses awsvpc, where each task has its own ENI and private IP and there is no host port. The target group must therefore be created with target type ip so ECS can register task IPs directly. A target group with type instance cannot be attached to a Fargate service, and the mistake surfaces as a CreateService validation error.
You never call the ALB or Cloud Map registration APIs yourself. When a task reaches a healthy state, ECS registers it in the target group and any serviceRegistries. When a task drains during a deployment or stops, ECS deregisters it - respecting the target group's deregistration delay so in-flight requests finish. This automatic reconciliation is the whole point of attaching discovery at the service level rather than wiring tasks by hand.
A load-balanced service should set healthCheckGracePeriodSeconds. Without it, the ALB begins health checks the moment a task registers, and a task still booting is deregistered as unhealthy before it can serve - a false failure that looks like a crash loop. Set the grace period above your worst-case cold start so ECS ignores ALB health during warm-up.
For awsvpc tasks, an A record (Type: "A") maps the service name to task IPs, which is what most callers want. Use an SRV record when callers also need the port, for example dynamic-port workloads. Keep the DNS TTL short (10-30 seconds) so clients pick up membership changes quickly rather than caching a stale set of IPs.
Instance target type on a Fargate service. Fargate needs an ip target group; an instance one fails CreateService. Create the target group with the right type up front.
containerName/containerPort mismatch. They must match a portMapping in the task definition, or registration fails.
No grace period behind an ALB. Slow starters get deregistered before they are ready. Set healthCheckGracePeriodSeconds.
Security groups block the health check. The task's security group must allow the ALB (or VPC) to reach the container port, or every target shows unhealthy.
Namespace creation is asynchronous.CreatePrivateDnsNamespace returns an operation id; the NamespaceId is not usable until the operation completes. Poll before creating the discovery service.
Long DNS TTL for internal discovery. A high TTL makes clients cache stale IPs after a deploy. Keep it short so membership changes propagate.
ECS Service Connect is a newer, higher-level alternative to raw Cloud Map that adds a proxy, built-in retries, and traffic telemetry; consider it for new internal meshes.
Network Load Balancer suits TCP/UDP or ultra-low-latency needs and also uses ip targets with Fargate; use it instead of an ALB when you do not need HTTP-layer routing.
Internal ALB rather than Cloud Map gives HTTP routing and health checks for internal callers too, at higher cost than plain DNS discovery.
Use an ALB for public HTTP traffic and Cloud Map (or Service Connect) for internal service-to-service calls; combine them for a typical public-API-plus-internal-services architecture.
How do I put my ECS service behind a load balancer?
Pass loadBalancers to CreateService with the targetGroupArn, containerName, and containerPort. ECS registers each task's IP into the target group and load-balances across them.
Why must the target group use ip targets for Fargate?
Fargate uses awsvpc networking, giving each task its own ENI and private IP with no host port. Only an ip target group can register those task IPs; an instance target group is incompatible.
How do internal services find each other on ECS?
With AWS Cloud Map. Create a private DNS namespace and a discovery service, then reference its ARN in the ECS service's serviceRegistries. Callers resolve a private DNS name to the current task IPs.
Do I have to register and deregister tasks myself?
No. ECS does it automatically. As tasks become healthy or stop, ECS updates the target group and Cloud Map registrations for you, honoring deregistration delay so in-flight requests finish.
What is the difference between an ALB and Cloud Map here?
An ALB handles external HTTP/HTTPS traffic with load balancing and routing. Cloud Map handles internal discovery via DNS records that resolve to task IPs. Many systems use both together.
Why are all my targets unhealthy?
Most often the task's security group does not allow the ALB or VPC to reach the container port, or the health check path is wrong, or there is no grace period so tasks are checked before they finish booting.
Should I use an A record or an SRV record in Cloud Map?
Use an A record when callers need only the IP, which covers most awsvpc services. Use an SRV record when callers also need the port, such as dynamic-port workloads.
What is ECS Service Connect?
A newer, higher-level service-to-service networking feature built on Cloud Map that adds a managed proxy, retries, and traffic telemetry. It is worth considering over raw Cloud Map for new internal meshes.
Why does my CreateService fail on the load balancer config?
Common causes are a mismatched containerName/containerPort versus the task definition, or an instance-type target group used with Fargate. Check both against the task definition and target group.
How quickly do clients see a new task via Cloud Map?
As fast as the DNS TTL allows. Keep the record TTL short (10-30 seconds) so clients re-resolve promptly after tasks are added or removed rather than caching stale IPs.