Application Load Balancer: Listener Rules, Target Groups, and Health Check Configuration
ALB routes HTTP/HTTPS traffic using content-based rules — path, hostname, headers, query strings. The listener evaluates rules in priority order and forwards to a target group. Health check configuration determines when targets are considered unhealthy and removed from rotation.
How ALB routes traffic
ALB is a Layer 7 (HTTP/HTTPS) load balancer. It terminates TLS, reads the HTTP request, evaluates listener rules, and forwards to a target group. The key distinction from NLB (Layer 4) is that ALB can make routing decisions based on request content.
Traffic flow:
- Client connects to ALB DNS name (or custom domain via Route 53 alias)
- Listener on port 80 or 443 receives the connection
- ALB terminates TLS (if HTTPS), decrypts the request
- Listener rules evaluate in priority order — first match wins
- Matched rule forwards to a target group
- Target group selects a healthy target using the load balancing algorithm
- ALB forwards the request to the selected target
Listeners, rules, and target groups
ConceptAWS ALBA listener is a process that checks for connection requests using the configured protocol and port. Each listener has rules that evaluate conditions and take actions (forward, redirect, return fixed response). Rules target groups contain the actual EC2 instances, IP addresses, Lambda functions, or ECS tasks that receive traffic.
Prerequisites
- HTTP/HTTPS
- TCP ports
- VPC subnets
- EC2 instances
Key Points
- Each ALB needs a listener on at least one port (typically 80 and 443).
- Rules have priority (1-50000). Lower number = higher priority. Default rule (no conditions) is last.
- One target group can be shared across multiple rules. Multiple ALBs can target the same group.
- Internet-facing ALB requires public subnets. Internal ALB can use private subnets.
Content-based routing rules
ALB rules can match on: host header, path, HTTP headers, query strings, HTTP method, and source IP. Rules are evaluated in priority order:
resource "aws_lb_listener_rule" "api_v2" {
listener_arn = aws_lb_listener.https.arn
priority = 100
condition {
path_pattern {
values = ["/api/v2/*"]
}
}
condition {
http_header {
http_header_name = "X-API-Version"
values = ["2.0", "2.1"]
}
}
action {
type = "forward"
target_group_arn = aws_lb_target_group.api_v2.arn
}
}
resource "aws_lb_listener_rule" "api_v1" {
listener_arn = aws_lb_listener.https.arn
priority = 200
condition {
path_pattern {
values = ["/api/*"]
}
}
action {
type = "forward"
target_group_arn = aws_lb_target_group.api_v1.arn
}
}
# Redirect HTTP to HTTPS (on the HTTP listener)
resource "aws_lb_listener_rule" "redirect_https" {
listener_arn = aws_lb_listener.http.arn
priority = 100
condition {
path_pattern {
values = ["/*"]
}
}
action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
Target groups and health checks
A target group defines the targets and how to check their health. A target is unhealthy if health checks fail unhealthy_threshold_count consecutive times:
resource "aws_lb_target_group" "api" {
name = "api-targets"
port = 8080
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "ip" # "instance" for EC2, "ip" for ECS awsvpc, "lambda" for Lambda
health_check {
enabled = true
path = "/health"
port = "traffic-port" # same port as target
protocol = "HTTP"
healthy_threshold = 2 # 2 consecutive successes = healthy
unhealthy_threshold = 3 # 3 consecutive failures = unhealthy
timeout = 5 # seconds to wait for response
interval = 30 # seconds between checks
matcher = "200-299" # 2xx = healthy
}
deregistration_delay = 30 # seconds to drain connections before target removal
}
Health check path: should return 200 quickly (under timeout seconds) with minimal resource usage. Don't health-check a path that queries the database — a slow DB causes health check failures, causing ALB to take the target out of rotation, potentially cascading.
Deregistration delay: when a target is deregistered (removed from the group or marked unhealthy), ALB waits this many seconds to drain in-flight connections before stopping traffic to it. Default 300s. Reduce it for faster deployments in ECS rolling updates.
💡Weighted target groups: traffic shifting for blue/green deployments
ALB supports weighted forwarding to shift traffic between two target groups — useful for canary deployments and blue/green rollouts:
resource "aws_lb_listener_rule" "weighted" {
listener_arn = aws_lb_listener.https.arn
priority = 100
condition {
path_pattern {
values = ["/api/*"]
}
}
action {
type = "forward"
forward {
target_group {
arn = aws_lb_target_group.api_v1.arn
weight = 90 # 90% to v1
}
target_group {
arn = aws_lb_target_group.api_v2.arn
weight = 10 # 10% to v2 (canary)
}
stickiness {
enabled = true
duration = 600 # once a client hits v2, stick to v2 for 10 min
}
}
}
}
Gradually shift weight: 90/10 → 70/30 → 50/50 → 0/100. Stickiness ensures a single client's session doesn't flip between versions mid-flow.
Stickiness uses a cookie (AWSALB for instance targets, AWSALBAPP for application-managed stickiness). Clients without cookie support (health checkers, some API clients) won't get sticky behavior.
Internet-facing vs internal ALB
Internet-facing: ALB is assigned public IPs. Must be deployed in public subnets (subnets with route to internet gateway). DNS resolves to public IPs. Clients from the internet can reach it.
Internal: ALB is assigned private IPs only. Deployed in private subnets. DNS resolves to private IPs. Only accessible within the VPC or from peered/connected networks.
resource "aws_lb" "public_api" {
name = "public-api"
internal = false # internet-facing
load_balancer_type = "application"
subnets = aws_subnet.public[*].id # public subnets required
security_groups = [aws_security_group.alb_public.id]
}
resource "aws_lb" "internal_services" {
name = "internal-services"
internal = true # internal only
load_balancer_type = "application"
subnets = aws_subnet.private[*].id # private subnets
security_groups = [aws_security_group.alb_internal.id]
}
The scheme (internal attribute) cannot be changed after creation — it requires destroying and recreating the ALB.
An ALB has two listener rules: rule 1 (priority=100) matches path '/api/*' and forwards to target group A. Rule 2 (priority=200) matches host 'api.example.com' and forwards to target group B. A request arrives: GET /api/users with Host: api.example.com. Which target group receives the request?
easyBoth rules apply to the same HTTPS listener. Target group A contains the v2 API. Target group B contains the legacy API. The ALB has a default rule forwarding to target group C.
ATarget group C — the default rule handles it because the request matches both rules and neither takes precedence
Incorrect.Matching multiple rules doesn't cause fallthrough to the default. The first matching rule (lowest priority number) wins.BTarget group A — rule 1 has priority 100, which evaluates before rule 2 (priority 200). The request matches rule 1's path condition, so it forwards to target group A
Correct!ALB evaluates rules in priority order — lower number = higher priority = evaluated first. Rule 1 (priority 100) matches path '/api/*' and this request's path is '/api/users'. Rule evaluation stops at the first match. Target group A receives the request. Rule 2 is never evaluated.CTarget group B — hostname matching takes precedence over path matching
Incorrect.ALB doesn't prioritize by condition type. Priority is solely determined by the rule's priority number, not the condition types in the rule.DBoth target groups receive the request — ALB sends to all matching targets
Incorrect.ALB forwards to exactly one target group per request. It doesn't fan out to multiple groups.
Hint:Rules are evaluated in priority order. Lower priority number = evaluated first. First match wins.