AWS ALB Target Group Unhealthy — Every Cause and Fix
Your ALB shows targets as unhealthy and traffic isn't reaching your app. Here's every reason target health checks fail and exactly how to fix each one.
ALB target shows unhealthy. Users get 502 or 503. The app seems to be running but traffic isn't reaching it.
Here's every reason this happens.
Diagnose First
# Check target health
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:...
# Output shows:
# State: unhealthy
# Reason: Target.FailedHealthChecks
# Description: Health checks failed with these codes: [200]In AWS Console: EC2 → Load Balancers → Your ALB → Target Groups → Targets tab.
Case 1: Wrong Health Check Path (Most Common)
Your health check is hitting / but the app returns 404 there. Or the health check expects /health but your app uses /healthz.
Symptom: Health checks failed with these codes: [404] or [301]
Fix:
# Update health check path
aws elbv2 modify-target-group \
--target-group-arn arn:aws:elasticloadbalancing:... \
--health-check-path /health \
--health-check-interval-seconds 30 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 3Make sure your app's /health endpoint:
- Returns HTTP 200 (not 301/302 redirect)
- Responds quickly (within health check timeout)
- Works without authentication
Case 2: Security Group Blocking Health Checks
The ALB can't reach the target because the security group doesn't allow traffic from the ALB.
Symptom: Target.Timeout — health check timed out
Fix: Add inbound rule to your instance/ECS task security group:
# Get ALB security group ID
ALB_SG=$(aws elbv2 describe-load-balancers \
--names my-alb \
--query 'LoadBalancers[0].SecurityGroups[0]' \
--output text)
# Allow health check port from ALB security group
aws ec2 authorize-security-group-ingress \
--group-id sg-target-group-id \
--protocol tcp \
--port 8080 \
--source-group $ALB_SGCase 3: App Not Listening on Correct Port
The target group says port 8080, but your app is listening on 3000.
Symptom: Target.ConnectionError — Error connecting to target
Fix:
# Check what port your app is actually on
# For ECS:
aws ecs describe-tasks --cluster my-cluster --tasks <task-id> \
--query 'tasks[0].containers[0].networkBindings'
# For EC2:
ssh ec2-user@instance-ip "ss -tlnp | grep LISTEN"Update the target group port to match your app, or fix the app to listen on the expected port.
Case 4: ECS Service Health Check Grace Period Too Short
ECS tasks need time to start. If the health check starts immediately, the app isn't ready yet.
Symptom: Tasks keep cycling — health check fires before app starts, task marked unhealthy, ECS terminates and restarts.
Fix:
{
"loadBalancers": [
{
"targetGroupArn": "arn:aws:...",
"containerName": "app",
"containerPort": 8080
}
],
"healthCheckGracePeriodSeconds": 120
}aws ecs update-service \
--cluster my-cluster \
--service my-service \
--health-check-grace-period-seconds 120Case 5: HTTPS App Returning SSL Error
Target group is using HTTPS protocol but your app's cert is self-signed or expired.
Symptom: Target.FailedHealthChecks — SSL handshake error
Fix option 1: Change target group protocol to HTTP (terminate SSL at ALB):
# Create new target group with HTTP (ALB handles TLS)
aws elbv2 create-target-group \
--name my-targets-http \
--protocol HTTP \
--port 8080 \
--vpc-id vpc-xxxxxFix option 2: Disable SSL verification for health checks (not for traffic):
In Console: Target Group → Health checks → Advanced → Success codes → Add your app's response code.
Case 6: VPC/Subnet Routing Issue
Target is in a private subnet with no route to ALB, or in a different VPC.
Symptom: Target.NotInUse — Target is not in the same VPC
Fix: Targets must be in the same VPC as the ALB. For cross-VPC, use VPC peering + register by IP.
# Register target by IP (for cross-AZ or private subnet scenarios)
aws elbv2 register-targets \
--target-group-arn arn:aws:... \
--targets Id=10.0.1.100,Port=8080Verification After Fix
# Wait for health check to pass (up to 30 seconds * unhealthy threshold)
watch -n 5 "aws elbv2 describe-target-health \
--target-group-arn arn:aws:... \
--query 'TargetHealthDescriptions[*].[Target.Id,TargetHealth.State]' \
--output table"
# Test directly
curl -v http://your-alb-dns/healthHealth Check Best Practices
# Recommended settings for most apps
aws elbv2 modify-target-group \
--target-group-arn arn:aws:... \
--health-check-path /health \
--health-check-interval-seconds 15 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 2 \ # 2 successes to mark healthy
--unhealthy-threshold-count 3 # 3 failures to mark unhealthyThe most common cause is Case 1 (wrong path) or Case 2 (security group). Check those first.
Learn AWS networking and load balancing with hands-on labs at KodeKloud.
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
AWS ALB 504 Gateway Timeout — Every Cause and Fix (2026)
Your ALB returns 504 Gateway Timeout but the app seems fine. Here's every reason this happens — backend timeouts, keepalive mismatches, health check failures — and exactly how to fix each one.
AWS ALB Showing Unhealthy Targets — How to Fix It
Fix AWS Application Load Balancer unhealthy targets. Covers health check misconfigurations, security group issues, target group problems, and EKS-specific ALB controller debugging.
AWS CloudFront 403 Forbidden — Every Cause and Fix (2026)
CloudFront returns 403 Forbidden but your S3 bucket or origin looks fine. Here's every cause — OAC misconfiguration, bucket policy missing, wrong origin domain, geo-restriction — and the exact fix.