🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoysJun 1, 20264 min read
Share:Tweet

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

bash
# 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:

bash
# 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 3

Make 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:

bash
# 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_SG

Case 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:

bash
# 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:

json
{
  "loadBalancers": [
    {
      "targetGroupArn": "arn:aws:...",
      "containerName": "app",
      "containerPort": 8080
    }
  ],
  "healthCheckGracePeriodSeconds": 120
}
bash
aws ecs update-service \
  --cluster my-cluster \
  --service my-service \
  --health-check-grace-period-seconds 120

Case 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):

bash
# 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-xxxxx

Fix 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.

bash
# 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=8080

Verification After Fix

bash
# 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/health

Health Check Best Practices

bash
# 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 unhealthy

The 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

Browse fixes
Newsletter

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

Comments