Health check của ALB là request HTTP thật do load balancer gửi tới target, nên "app chạy" trên máy chưa đủ. Kiểm tra theo thứ tự:
1. Security group của target có cho phép traffic từ security group của ALB trên đúng traffic port không. Đây là nguyên nhân phổ biến nhất — nên tham chiếu SG-của-ALB thay vì mở CIDR.
2. Port: health check port mặc định là traffic-port. App nghe 3000 nhưng target group đăng ký cổng 80 thì check luôn fail.
3. Path và mã trả về: mặc định check / và coi 200 là healthy. Nếu / trả 302 (redirect sang login) hoặc 404 thì unhealthy. Tạo endpoint /healthz trả 200 gọn nhẹ, hoặc chỉnh Matcher.
4. App bind địa chỉ nào: nghe 127.0.0.1 thay vì 0.0.0.0 thì chỉ chính máy đó gọi được — đúng với triệu chứng "curl trên máy thì được".
5. Ngưỡng thời gian: app khởi động chậm hơn interval × unhealthy threshold sẽ bị kill rồi thay mới liên tục. Nới HealthCheckIntervalSeconds/threshold, và với ECS thì đặt healthCheckGracePeriodSeconds.
6. Health check bị chính nó làm chậm: endpoint health mà đi query DB, DB chậm là cả fleet rớt khỏi LB. Health check nên nông.
Đọc lý do trực tiếp trong console ở cột health status details (vd Request timed out, Health checks failed with these codes: [404]) — mỗi thông báo chỉ thẳng vào một trong các nhóm trên. Khi rollback/deploy, nhớ deregistration delay (mặc định 300s) là lý do target ở trạng thái draining lâu.