AWS ALB Target Group Registration Fails with Unhealthy
Your EC2 instances stay unhealthy in an ALB target group. Usually a security group or health check path issue. Here's the real fix.
You add an EC2 instance to an AWS Application Load Balancer target group. The status shows "initial" for a few seconds, then switches to "unhealthy". The load balancer never routes traffic to your backend. This happens most often after you launch a fresh instance or change the security group. The health check request from the ALB gets no response or gets a wrong status code.
Why this happens
The ALB health check is a simple HTTP GET request from the load balancer to your instance. If your security group doesn't allow that request, the instance can't respond. If the health check path returns anything besides a 200 or 302, the instance is marked unhealthy. Common triggers:
- Security group on the EC2 instance blocks the ALB's source IP range
- Health check path is set to
/but your app only serves/health - Your app returns a 404 or 500 on the health check path
- The instance's OS firewall (iptables, ufw) blocks the port
The real fix
Don't waste time restarting the instance or rebuilding the target group. The fix is always in the network or app config. Follow these steps in order.
- Check the security group inbound rules on the EC2 instance.
- Verify the health check path and port in the target group.
- Test the health check manually from the instance.
- Check the OS firewall.
- Look at the ALB access logs.
Go to EC2 console, select your instance, check the security group. Add a rule to allow TCP on the health check port (usually 80 or 443) from the ALB's source security group. If you have multiple ALBs, use the ALB's security group ID, not 0.0.0.0/0. The rule should look like this:
Type: HTTP
Protocol: TCP
Port: 80
Source: sg-xxxxxxxx (your ALB's security group)
Open the target group settings. The health check path must match a URL your app actually serves. If your app is a Node.js server with app.get('/health', ...), set the path to /health. Don't use / unless you're sure that returns 200. Also check the port matches what your app listens on.
SSH into the instance. Run this command to simulate the ALB's request:
curl -v http://localhost:80/health
If you get a 200 response, the app is fine. If you get connection refused, the app isn't running or port is wrong. If you get a different status code, fix the app route.
On Linux, run sudo iptables -L or sudo ufw status. Make sure the health check port isn't blocked. AWS security groups act as a firewall, but the instance's own firewall can also block traffic.
If you have access logs enabled, check the logs for health check requests. They appear with elb_status_code of 200 or 503. If you see 503, the ALB can reach the instance but the response is bad. If you see no entries at all, the request never reaches your instance — likely a security group issue.
What to check if it still fails
- Network ACLs. If you have custom NACLs on the subnet, they might block the ALB's traffic. NACLs are stateless, so you need inbound and outbound rules for both the health check request and response.
- VPC routing. Make sure the instance is in a public subnet with a route to an internet gateway, or the ALB is internal and the instance is in the same VPC.
- Multiple network interfaces. If the instance has more than one ENI, the health check might route to the wrong interface. Disable source/destination check on the ENI if needed.
- Application startup time. If your app takes longer than the health check interval to start, the instance will be unhealthy until the app is ready. Increase the health check interval or add a startup delay.
One more thing: if you're using Docker, make sure the container is running and port mapping matches the ALB's target port. I've seen people map container port 3000 to host port 80 but set the target group to port 3000. That mismatch is a common mistake.
The whole point of the health check is to make sure traffic goes only to working instances. If your instance stays unhealthy, don't guess. Work through the list above. Nine times out of ten, it's the security group rule.
Was this solution helpful?