Load balancer health checks
Pod lifecycle overview · Checklist and references
ALB/NLB Health Check and Probe Integration
When using AWS Load Balancer Controller, synchronize ALB/NLB health checks with Kubernetes Readiness Probes to enable zero-downtime deployments.
ALB Target Group Health Checks vs. Readiness Probes
Table 1. ALB and NLB health checks compared with Kubernetes Readiness Probes
| Aspect | ALB Health Check | NLB Health Check | Kubernetes Readiness Probe |
|---|---|---|---|
| Executed by | AWS Load Balancer | AWS Load Balancer | kubelet |
| Check target | IP:Port in the Target Group | IP:Port in the Target Group | Pod container |
| Action on failure | Remove from targets (block traffic) | Remove from targets (block traffic) | Remove from Service Endpoints |
| Default interval | 30 seconds | 30 seconds | 10 seconds |
| Timeout | 5 seconds | Protocol-dependent (10 seconds for TCP, 6 seconds for HTTP/HTTPS) | 1 second |
The ALB and NLB interval/timeout values are the Elastic Load Balancing Target Group API defaults; see the ALB and NLB health check documentation for the exact values. Target groups created by the AWS Load Balancer Controller can override these values through annotations, so they may differ from the ELB API defaults.
Health Check Timing Synchronization Strategy
During a rolling update, operations proceed in the following order:
Figure 1. Pod creation and termination timing with load balancer health checks during a rolling update
Recommended configuration:
apiVersion: v1
kind: Service
metadata:
name: myapp
annotations:
# ALB health check configuration
alb.ingress.kubernetes.io/healthcheck-path: /ready
alb.ingress.kubernetes.io/healthcheck-interval-seconds: "10"
alb.ingress.kubernetes.io/healthcheck-timeout-seconds: "5"
alb.ingress.kubernetes.io/healthy-threshold-count: "2"
alb.ingress.kubernetes.io/unhealthy-threshold-count: "2"
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
selector:
app: myapp
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:v1
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready # Same path as ALB
port: 8080
periodSeconds: 5 # Shorter interval than ALB
failureThreshold: 2
successThreshold: 1
terminationGracePeriodSeconds: 60
Pod Readiness Gates (Contributing to Zero-Downtime Deployments)
AWS Load Balancer Controller v2.5+ supports Pod Readiness Gates, delaying the transition to the Ready state until the Pod is registered as an ALB/NLB target and passes health checks. This feature contributes to zero-downtime deployments but does not by itself guarantee them. The following prerequisites must also hold for it to take effect: the Namespace must have the automatic-injection label applied, the LBC version must support readiness gates, the ALB/NLB health check path and thresholds must be configured correctly, and the terminating side must drain existing connections through preStop and connection draining.
Enabling the feature:
# Enable automatic injection by adding a label to the Namespace
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
elbv2.k8s.aws/pod-readiness-gate-inject: enabled
Verify operation:
# Check Pod Readiness Gates
kubectl get pod myapp-xyz -o yaml | grep -A 10 readinessGates
# Example output:
# readinessGates:
# - conditionType: target-health.alb.ingress.k8s.aws/my-target-group-hash
# Check Pod Conditions
kubectl get pod myapp-xyz -o jsonpath='{.status.conditions}' | jq
Benefits:
- During rolling updates, the old Pod is retained until it is removed from the targets
- The new Pod receives traffic only after passing ALB health checks
- When the prerequisites are met together, it greatly reduces traffic loss and contributes to zero-downtime deployments
For more information about Pod Readiness Gates, see the "Pod Readiness Gates" section of the EKS High Availability Architecture Guide.
Gateway API Health Check Integration (ALB Controller v2.14+)
AWS Load Balancer Controller v2.14+ integrates with the Kubernetes Gateway API, providing more advanced per-route health check mappings than Ingress. L7 routes (HTTPRoute/GRPCRoute) are served by an ALB on v2.14.0 and later, and L4 routes (TCPRoute/UDPRoute/TLSRoute) by an NLB on v2.13.3 and later.
Using the LBC and Gateway API together is not yet suggested for production workloads (per the LBC v2.14 Gateway documentation). It also requires prerequisites first: install the standard Gateway API CRDs (plus the experimental CRDs if needed) and the LBC Gateway-specific CRDs, and specify the --feature-gates=ALBGatewayAPI=true (L7) or NLBGatewayAPI=true (L4) flag in the LBC deployment. By default the LBC does not reconcile Gateway API resources.
Gateway API vs. Ingress Health Check Comparison
Table 2. Health check capabilities of Ingress compared with Gateway API
| Aspect | Ingress | Gateway API |
|---|---|---|
| Health check configuration location | Service/Ingress annotation | TargetGroupConfiguration CRD |
| Per-route health checks | Limited (annotation-based) | Native support (per HTTPRoute/GRPCRoute) |
| L4/L7 protocol support | HTTP/HTTPS only | Supports TCP/UDP/TLS/HTTP/GRPC |
| Multi-tenant role separation | Single Ingress object | Separate Gateway (infrastructure) and Route (application) |
| Weighted canary deployments | Supported via weighted forward-action annotations | Native HTTPRoute support |
Gateway API Architecture and Health Checks
Figure 2. Gateway API components and TargetGroupConfiguration application structure
L7 Health Checks: HTTPRoute/GRPCRoute with ALB
TargetGroupConfiguration CRD example:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: prod-gateway
namespace: production
spec:
gatewayClassName: alb
listeners:
- name: http
protocol: HTTP
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-v1-route
namespace: production
spec:
parentRefs:
- name: prod-gateway
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /api/v1
backendRefs:
- name: api-v1-service
port: 8080
---
# TargetGroupConfiguration (AWS Load Balancer Controller v2.14+)
apiVersion: gateway.k8s.aws/v1beta1
kind: TargetGroupConfiguration
metadata:
name: api-v1-healthcheck
namespace: production
spec:
targetReference:
name: api-v1-service
defaultConfiguration:
targetType: ip
routeConfigurations:
- routeIdentifier:
kind: HTTPRoute
namespace: production
name: api-v1-route
targetGroupProps:
healthCheckConfig:
healthCheckProtocol: HTTP
healthCheckPath: /api/v1/healthz # Per-route health checks
healthCheckPort: "8080"
healthCheckInterval: 10
healthCheckTimeout: 5
healthyThresholdCount: 2
unhealthyThresholdCount: 2
matcher:
httpCode: "200-299"
GRPCRoute health check example:
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: grpc-service-route
namespace: production
spec:
parentRefs:
- name: prod-gateway
hostnames:
- grpc.example.com
rules:
- matches:
- method:
service: myservice.v1.MyService
backendRefs:
- name: grpc-backend
port: 9090
---
apiVersion: gateway.k8s.aws/v1beta1
kind: TargetGroupConfiguration
metadata:
name: grpc-healthcheck
namespace: production
spec:
targetReference:
name: grpc-backend
defaultConfiguration:
targetType: ip
protocolVersion: GRPC
routeConfigurations:
- routeIdentifier:
kind: GRPCRoute
namespace: production
name: grpc-service-route
targetGroupProps:
healthCheckConfig:
healthCheckProtocol: HTTP # gRPC health checks use HTTP/2
healthCheckPath: /grpc.health.v1.Health/Check
healthCheckPort: "9090"
healthCheckInterval: 10
healthCheckTimeout: 5
healthyThresholdCount: 2
unhealthyThresholdCount: 2
matcher:
grpcCode: "0" # gRPC OK status
L4 Health Checks: TCPRoute/UDPRoute with NLB
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: tcp-service-route
namespace: production
spec:
parentRefs:
- name: nlb-gateway
sectionName: tcp-listener
rules:
- backendRefs:
- name: tcp-backend
port: 5432
---
apiVersion: gateway.k8s.aws/v1beta1
kind: TargetGroupConfiguration
metadata:
name: tcp-healthcheck
namespace: production
spec:
targetReference:
name: tcp-backend
defaultConfiguration:
targetType: ip
routeConfigurations:
- routeIdentifier:
kind: TCPRoute
namespace: production
name: tcp-service-route
targetGroupProps:
healthCheckConfig:
healthCheckProtocol: TCP # Check TCP connectivity only
healthCheckPort: "5432"
healthCheckInterval: 30
healthCheckTimeout: 10
healthyThresholdCount: 3
unhealthyThresholdCount: 3
Gateway API Pod Readiness Gates
Gateway API supports Pod Readiness Gates in the same way as Ingress:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
elbv2.k8s.aws/pod-readiness-gate-inject: enabled
Verify operation:
# Check Gateway status
kubectl get gateway prod-gateway -n production
# Check HTTPRoute status
kubectl get httproute api-v1-route -n production -o yaml
# Check Pod Readiness Gates
# The condition type the LBC injects is of the form target-health.elbv2.k8s.aws/<hash>, with a
# per-target-group suffix, so a fixed string cannot match it; grep the condition list for that prefix.
kubectl get pod -n production -l app=api-v1 -o yaml | grep -A 5 "target-health.elbv2.k8s.aws"
Health Check Transition Checklist for Migrating from Ingress to Gateway API
Table 3. Step-by-step health check verification items when migrating from Ingress to Gateway API
| Step | Ingress | Gateway API | Verification |
|---|---|---|---|
| 1. Health check path mapping | Annotation-based | TargetGroupConfiguration CRD | Separate policies for each route |
| 2. Protocol configuration | HTTP/HTTPS only | HTTP/HTTPS/GRPC/TCP/UDP | Verify protocol type |
| 3. Pod Readiness Gates | Namespace label | Namespace label (same) | Ensure zero-downtime deployments |
| 4. Health check timing | Service annotation | TargetGroupConfiguration | Validate interval/timeout |
| 5. Multi-route health checks | Single route only | Independent configuration per route | Validate each route |
Migration example (Ingress → Gateway API):
# Before (Ingress)
apiVersion: v1
kind: Service
metadata:
name: myapp
annotations:
alb.ingress.kubernetes.io/healthcheck-path: /healthz
alb.ingress.kubernetes.io/healthcheck-interval-seconds: "10"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 8080
# After (Gateway API)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp-route
spec:
parentRefs:
- name: prod-gateway
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: myapp
port: 8080
---
apiVersion: gateway.k8s.aws/v1beta1
kind: TargetGroupConfiguration
metadata:
name: myapp-healthcheck
spec:
targetReference:
name: myapp
defaultConfiguration:
targetType: ip
routeConfigurations:
- routeIdentifier:
kind: HTTPRoute
name: myapp-route
targetGroupProps:
healthCheckConfig:
healthCheckProtocol: HTTP
healthCheckPath: /healthz
healthCheckPort: "8080"
healthCheckInterval: 10
healthCheckTimeout: 5
healthyThresholdCount: 2
unhealthyThresholdCount: 2
- Phased migration: Ingress and Gateway API can run simultaneously on the same ALB (separate listeners)
- Canary deployment: Transition safely using weighted traffic splitting in HTTPRoute
- Rollback plan: Retain Ingress objects for a period after migration is complete