Introduction
AWS Application Load Balancers (ALB) distribute incoming HTTP/HTTPS traffic across registered targets—EC2 instances, containers, IP addresses, or Lambda functions. The ALB routes requests based on listener rules and target groups. When the ALB receives a request but has no healthy targets available to serve it, the ALB returns HTTP 503 Service Unavailable.
The 503 error indicates an issue with the backend targets, not the ALB itself. Common causes include all targets failing health checks, no targets registered in the target group, security groups blocking traffic, or health check misconfiguration that marks healthy targets as unhealthy. Understanding the target health lifecycle—from registration through health check evaluation to serving traffic—is essential for debugging ALB 503 errors.
ALB health checks are separate from application-level health endpoints. An application may be running and responding to requests, but if it doesn't pass ALB health checks (wrong path, wrong response code, timeout), the ALB won't route traffic to it.
Symptoms
When an ALB target group has no healthy targets, you will observe these symptoms:
- HTTP requests to the ALB return 503 Service Unavailable
- CloudWatch metric
HealthyHostCountis 0 or drops suddenly - Target group shows targets as
unhealthy,unused,draining, orinitial - Direct access to backend instances works but traffic through ALB fails
- Access logs show 503 responses from the ALB
- ALB error pages display "Service Unavailable"
ALB response to client:
``` HTTP/1.1 503 Service Unavailable Content-Type: text/html
<!DOCTYPE html> <html> <head><title>503 Service Unavailable</title></head> <body> <h1>503 Service Unavailable</h1> No server is available to handle this request. </body> </html> ```
AWS console showing target health:
Target Group: web-app-tg
Targets:
i-abc123 (10.0.1.10:8080) - Status: unhealthy
Health check: Failed
Reason: HTTP response code: 503
i-def456 (10.0.1.11:8080) - Status: unhealthy
Health check: Failed
Reason: Request timed outCloudWatch metrics:
HealthyHostCount: 0 (should be > 0)
HTTPCode_Target_5XX_Count: increasing
TargetResponseTime: elevated or N/ACommon Causes
Several factors cause ALB target groups to have no healthy targets:
- 1.Health check path mismatch: The health check endpoint returns a different status code than the expected matcher (e.g., application returns 200, matcher expects 200-299 but path is wrong).
- 2.Health check timeout too short: Slow application startup or response times exceed the health check timeout, causing timeouts even when the application is healthy.
- 3.Security group blocking ALB traffic: The target's security group doesn't allow inbound traffic from the ALB's security group or the ALB's private IP range.
- 4.Network ACL blocking health checks: VPC network ACLs block traffic between ALB subnets and target subnets.
- 5.No targets registered: Target group has no registered instances, or targets were deregistered during deployment without new targets registered.
- 6.Wrong health check port: Health check uses a different port than where the application listens.
- 7.Application not listening on expected port: Application crashed or isn't bound to the port configured in the target group.
- 8.Listener rule routing to wrong target group: Listener rules direct traffic to an empty or incorrect target group.
- 9.All targets in draining state: During deployment or scale-in, all targets are draining connections with no healthy targets remaining.
Step-by-Step Fix
Follow these steps to diagnose and resolve ALB 503 errors:
Step 1: Check target health status
Examine why targets are marked unhealthy:
```bash # Describe target health for the target group aws elbv2 describe-target-health \ --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/web-app/abc123def456
# Output showing unhealthy targets: { "TargetHealthDescriptions": [{ "Target": { "Id": "i-abc123", "Port": 8080 }, "TargetHealth": { "State": "unhealthy", "Reason": "Target.ResponseCodeMismatch", "Description": "Health checks failed with code: 404" } }] }
# Common reasons: # Target.ResponseCodeMismatch - Wrong status code # Target.Timeout - Health check timed out # Target.FailedHealthChecks - Repeated failures # Elb.InitialHealthChecking - Still initializing ```
Step 2: Test the health endpoint directly
Verify the application responds correctly:
```bash # Get target IP addresses aws elbv2 describe-target-health \ --target-group-arn $TG_ARN \ --query 'TargetHealthDescriptions[*].Target.Id'
# SSH into an EC2 instance in the same VPC, or use SSM # Test the health endpoint on the target curl -v http://10.0.1.10:8080/health
# Check if the application is listening ss -tlnp | grep 8080 netstat -tlnp | grep 8080
# Check application logs journalctl -u myapp -n 50 tail -100 /var/log/app/error.log ```
Step 3: Verify health check configuration
Check target group health check settings:
```bash # Get target group attributes aws elbv2 describe-target-groups \ --target-group-arns $TG_ARN
# Key settings to verify: # - HealthCheckPath: /health or /healthz # - HealthCheckPort: traffic-port or specific port # - HealthCheckProtocol: HTTP or HTTPS # - HealthCheckTimeoutSeconds: usually 5-10 # - HealthCheckIntervalSeconds: usually 30 # - HealthyThresholdCount: usually 3-5 # - UnhealthyThresholdCount: usually 2-3 # - Matcher.HttpCode: 200 or 200-299 ```
Modify health check if needed:
aws elbv2 modify-target-group \
--target-group-arn $TG_ARN \
--health-check-path /healthz \
--health-check-interval-seconds 30 \
--health-check-timeout-seconds 10 \
--healthy-threshold-count 3 \
--unhealthy-threshold-count 2 \
--matcher HttpCode=200Step 4: Check security groups
Verify security groups allow ALB traffic:
```bash # Get ALB security group aws elbv2 describe-load-balancers \ --names my-alb \ --query 'LoadBalancers[0].SecurityGroups'
# Get target security group aws ec2 describe-instances \ --instance-ids i-abc123 \ --query 'Reservations[0].Instances[0].SecurityGroups[*].GroupId'
# Check target security group rules aws ec2 describe-security-groups \ --group-ids sg-target \ --query 'SecurityGroups[0].IpPermissions'
# Ensure the target SG allows inbound from ALB SG on the target port # Expected rule: # From: sg-alb (ALB security group) # Port: 8080 (application port) # Protocol: TCP
# Add rule if missing aws ec2 authorize-security-group-ingress \ --group-id sg-target \ --protocol tcp \ --port 8080 \ --source-group sg-alb ```
Step 5: Check network ACLs
Verify NACLs don't block traffic:
```bash # Get subnet associations for targets aws ec2 describe-instances \ --instance-ids i-abc123 \ --query 'Reservations[0].Instances[0].SubnetId'
# Get NACL for the subnet aws ec2 describe-network-acls \ --filters "Name=association.subnet-id,Values=subnet-target"
# Check NACL rules # Ensure inbound allows traffic from ALB subnets on app port # Ensure outbound allows responses to ALB (ephemeral ports)
# NACLs are stateless - both directions must be allowed ```
Step 6: Verify listener rules
Check ALB listener configuration:
```bash # Get listeners for the ALB aws elbv2 describe-listeners \ --load-balancer-arn $ALB_ARN
# Get listener rules aws elbv2 describe-rules \ --listener-arn $LISTENER_ARN
# Verify the default action forwards to correct target group # Check if path-based routing sends traffic to wrong target group
# Modify listener if needed aws elbv2 modify-listener \ --listener-arn $LISTENER_ARN \ --default-actions Type=forward,TargetGroupArn=$TG_ARN ```
Step 7: Check for target registration issues
Verify targets are properly registered:
```bash # List registered targets aws elbv2 describe-target-health \ --target-group-arn $TG_ARN \ --query 'TargetHealthDescriptions[*].Target'
# Register targets if missing aws elbv2 register-targets \ --target-group-arn $TG_ARN \ --targets Id=i-abc123,Port=8080 Id=i-def456,Port=8080
# Check if targets are in the right target group aws elbv2 describe-target-groups \ --load-balancer-arn $ALB_ARN \ --query 'TargetGroups[*].TargetGroupArn' ```
Step 8: Adjust for slow startup
If application needs time to become healthy:
```bash # Increase health check grace period aws elbv2 modify-target-group-attributes \ --target-group-arn $TG_ARN \ --attributes Key=deregistration_delay.timeout_seconds,Value=300
# For ECS services, adjust health check grace period aws ecs update-service \ --cluster my-cluster \ --service my-service \ --health-check-grace-period-seconds 120 ```
Verification
After fixing the issue, verify targets are healthy:
```bash # Check target health aws elbv2 describe-target-health \ --target-group-arn $TG_ARN \ --query 'TargetHealthDescriptions[*].{Target: Target.Id, State: TargetHealth.State, Reason: TargetHealth.Reason}'
# Expected: State "healthy" for all targets
# Test ALB endpoint curl -I http://my-alb-123456.us-east-1.elb.amazonaws.com/
# Expected: HTTP/1.1 200 OK (not 503)
# Check CloudWatch metrics aws cloudwatch get-metric-statistics \ --namespace AWS/ApplicationELB \ --metric-name HealthyHostCount \ --dimensions Name=TargetGroup,Value=targetgroup/web-app/abc123 Name=LoadBalancer,Value=app/my-alb/def456 \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 60 \ --statistics Average # Expected: HealthyHostCount > 0 ```
Prevention
To prevent ALB 503 errors:
- 1.Use dedicated health check endpoints: Create lightweight endpoints specifically for health checks.
```python @app.route('/healthz') def health(): return 'OK', 200
# Not: @app.route('/') def index(): return render_template('index.html') # Too heavy for health checks ```
- 1.Set appropriate health check timeouts: Account for application startup time.
# For applications with slow startup
aws elbv2 modify-target-group \
--target-group-arn $TG_ARN \
--health-check-timeout-seconds 15 \
--health-check-interval-seconds 60 \
--healthy-threshold-count 5- 1.Monitor HealthyHostCount: Set up alerts before reaching 0.
aws cloudwatch put-metric-alarm \
--alarm-name alb-low-healthy-hosts \
--metric-name HealthyHostCount \
--namespace AWS/ApplicationELB \
--dimensions Name=TargetGroup,Value=$TG_NAME Name=LoadBalancer,Value=$ALB_NAME \
--threshold 2 \
--comparison-operator LessThanThreshold \
--evaluation-periods 2 \
--period 60- 1.Implement graceful deployment: Ensure new targets are healthy before deregistering old ones.
# Use target group with multiple AZs and proper scaling
# Enable connection draining
aws elbv2 modify-target-group-attributes \
--target-group-arn $TG_ARN \
--attributes Key=deregistration_delay.timeout_seconds,Value=300- 1.Document health check requirements: Keep configuration documented.
## Target Group: web-app-tg
- Health Check: GET /healthz:8080
- Expected Response: 200 OK
- Timeout: 10 seconds
- Interval: 30 seconds
- Security Group: Allow from sg-alb on port 8080Related Articles
- [AWS troubleshooting: Fix IAM Permission Denied - Complete Tro](fix-iam-permission-denied)
- [AWS cloud troubleshooting: AWS ACM Certificate Pending Validation Because the](aws-acm-certificate-pending-validation-wrong-route53-zone)
- [AWS cloud troubleshooting: AWS ALB Returns 502 Because the Target Closed the ](aws-alb-502-target-closed-connection-keepalive-timeout-mismatch)
- [AWS cloud troubleshooting: Fix AWS ALB CreateListener TargetGroupNotFound Err](aws-alb-createlistener-targetgroupnotfound)
- [AWS cloud troubleshooting: Fix Aws Alb Lambda 502 Bad Gateway Issue in AWS](aws-alb-lambda-502-bad-gateway)
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "AWS cloud troubleshooting: AWS ALB Target Group Returned 503 Service Unavaila", "description": "Professional guide to fix AWS ALB Target Group Returned 503 Service Unavailable. AWS cloud troubleshooting with step-by-step solutions. Learn best practices and prevention strategies.", "url": "https://www.fixwikihub.com/aws-alb-target-group-503-service-unavailable", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-01-22T15:36:39.883Z", "dateModified": "2026-01-22T15:36:39.883Z" } </script>