ALB Port Configuration: Listener Port, Target Group Port, and Per-Instance Port Overrides
An ALB has four port-related settings: the listener port, target group default port, per-instance port, and health check port. They're independent and serve different purposes. The target group port acts as a default that per-instance overrides replace — which is how ECS dynamic port mapping works.
Four port settings and what they each control
An ALB involves four distinct port settings:
- Listener port: the port the ALB listens on for incoming client traffic (typically 80 and 443)
- Target group port: the default port ALB forwards traffic to, used when a target doesn't specify its own port
- Target instance port: the specific port ALB uses for a particular registered target (overrides target group port)
- Health check port: the port used for health check requests (defaults to target's traffic port)
Target group port vs per-instance port
ConceptAWS ALBThe target group port is a default value. When you register a target (EC2 instance or IP) without specifying a port, the target group port is used. When you register a target with an explicit port, that port overrides the target group port for that specific target. The target group port itself is meaningless if all targets specify their own ports.
Prerequisites
- ALB listeners and target groups
- EC2 instances and ports
- ECS task networking
Key Points
- Target group port required at creation but acts only as default — not a global override.
- Per-instance port allows multiple instances to use different ports in the same target group.
- ECS dynamic port mapping uses per-instance port: host port is dynamically assigned, registered to target group with that specific port.
- Health check port 'traffic-port' means use the same port as the registered target's traffic port.
Static port configuration (most common)
When all targets in a target group use the same port (a typical setup):
resource "aws_lb_target_group" "api" {
name = "api"
port = 8080 # all targets use port 8080
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "instance"
health_check {
port = "traffic-port" # health check on the same port as traffic (8080)
path = "/health"
}
}
resource "aws_lb_target_group_attachment" "api_1" {
target_group_arn = aws_lb_target_group.api.arn
target_id = aws_instance.api_1.id
# No port specified → uses target group default port (8080)
}
resource "aws_lb_target_group_attachment" "api_2" {
target_group_arn = aws_lb_target_group.api.arn
target_id = aws_instance.api_2.id
# Same — uses 8080
}
Per-instance port override: running multiple containers per EC2 instance
When multiple containers run on the same EC2 instance (bridge network mode in ECS), each container gets a dynamically assigned host port. Registering all containers to the same target group requires per-instance port overrides:
EC2 instance (10.0.1.5):
Container A: app port 8080 → host port 32768
Container B: app port 8080 → host port 32769
Container C: app port 8080 → host port 32770
All three register to the same target group with different ports:
Target group registration:
Target: 10.0.1.5:32768
Target: 10.0.1.5:32769
Target: 10.0.1.5:32770
ECS handles this automatically when you configure the service with an ALB. The ECS agent registers task containers with the correct dynamic host port at task startup and deregisters at task shutdown.
resource "aws_lb_target_group" "api" {
name = "api"
port = 8080 # this value only matters if a target is registered without explicit port
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "instance" # "ip" for awsvpc mode (Fargate, ECS with awsvpc)
}
# ECS service automatically handles target registration with dynamic ports
resource "aws_ecs_service" "api" {
name = "api"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.api.arn
load_balancer {
target_group_arn = aws_lb_target_group.api.arn
container_name = "api"
container_port = 8080 # container's port, not host port
}
}
The container definition specifies containerPort: 8080 with no hostPort (or hostPort: 0), which causes ECS to assign a random host port. When the task starts, ECS registers the task with the ALB target group using the actual assigned host port.
📝awsvpc mode: no dynamic port mapping needed
ECS awsvpc network mode (used by Fargate and configurable for EC2) gives each task its own ENI with its own IP address. There's no host port concept — the container port is directly accessible on the task's private IP.
With awsvpc mode, all tasks can use the same port (8080) because each task is on a different IP. The target group registers tasks by IP:
resource "aws_lb_target_group" "api" {
name = "api"
port = 8080
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "ip" # required for awsvpc/Fargate
}
ECS registers tasks as IP targets: 10.0.1.5:8080, 10.0.1.6:8080, 10.0.1.7:8080 — all port 8080 because each is a unique IP.
With instance mode (bridge networking), you need dynamic port mapping and per-instance port overrides to run multiple tasks on the same host. With awsvpc mode, it's unnecessary — each task is isolated by IP.
Health check port configuration
The health check port defaults to traffic-port (same as where traffic goes). Override it if your health endpoint is on a different port than your traffic endpoint:
health_check {
port = "8081" # health check on management port
path = "/health"
protocol = "HTTP"
# Traffic goes to port 8080, but health checks go to 8081
# This keeps health check traffic separate from production traffic
}
Separate health check ports are useful when your application's management interface (metrics, health) is on a different port than the main application traffic. It also isolates health check traffic from production load.
An ECS service using bridge network mode runs 3 tasks on a single EC2 instance. Each task uses containerPort=8080 with no hostPort set. The ALB target group has port=8080. After deployment, the ALB shows all three targets as unhealthy. kubectl describes the targets as registered on ports 32771, 32772, and 32773. What is the likely cause?
mediumThe health check path is /health and returns 200 when accessed directly. The ALB security group allows traffic from the internet. The target group health check port is traffic-port.
AThe EC2 instance security group doesn't allow traffic on the dynamically assigned ports 32771-32773 from the ALB
Correct!With dynamic port mapping, ECS assigns random host ports. The ALB sends health check and traffic to these ports (32771, 32772, 32773). The EC2 instance's security group must allow traffic from the ALB security group on the ephemeral port range (typically 32768-65535). If the security group only allows port 8080, all health checks fail. Fix: update the EC2 instance security group to allow TCP 32768-65535 from the ALB security group.BThe target group port (8080) must match the host ports exactly
Incorrect.The target group port is a default; it doesn't need to match the per-instance ports. The actual registered ports (32771-32773) are what the ALB uses to send health checks.CECS bridge mode doesn't support ALB integration
Incorrect.ECS bridge mode with dynamic port mapping is fully supported with ALB. It's a well-established pattern for running multiple tasks per instance.DThe health check path /health is not accessible on the dynamic ports
Incorrect.The application listens on containerPort 8080, which is mapped to the dynamic host port. When the ALB hits port 32771, it reaches the container's port 8080 — including the /health endpoint. The path isn't the issue; the security group is.
Hint:The ALB is sending health checks to the dynamically assigned ports. What might block traffic to those specific ports?