ALB vs NLB vs GWLB — AWS Load Balancers Decoded
You've got three web servers running behind no load balancer. A user's request hits server 1, which is already at 95% CPU, while servers 2 and 3 are idle. Another user gets a timeout because server 1 crashed and there's nothing to redirect the traffic. Load balancers solve both problems — but AWS gives you three types, and picking the wrong one means paying more for less or missing features you actually need.
Layer 4 vs Layer 7 — Why It Matters
Load balancers operate at different layers of the OSI model, and this determines what they can see and do:
Layer 4 (Transport) sees TCP/UDP packets. It knows source IP, destination IP, and port numbers. It cannot inspect HTTP headers, URLs, or cookies. It's fast because it doesn't parse application data.
Layer 7 (Application) sees HTTP/HTTPS requests. It knows URLs, headers, cookies, query strings, and request bodies. It can make routing decisions based on content. It's slightly slower because it parses the request.
The Three Load Balancers
| Feature | ALB (Application) | NLB (Network) | GWLB (Gateway) |
|---|---|---|---|
| Layer | 7 (HTTP/HTTPS) | 4 (TCP/UDP/TLS) | 3 (IP packets) |
| Protocols | HTTP, HTTPS, gRPC, WebSocket | TCP, UDP, TLS | IP (GENEVE encapsulation) |
| Latency | ~ms range | ~100us (ultra-low) | ~ms range |
| Static IP | No (use Global Accelerator) | Yes (one per AZ) | No |
| SSL termination | Yes | Yes (TLS) | No |
| Path-based routing | Yes | No | No |
| Host-based routing | Yes | No | No |
| Lambda targets | Yes | No | No |
| Preserve source IP | Via X-Forwarded-For | Yes (native) | Yes |
| PrivateLink | No | Yes | No |
| Cost (approx) | $0.0225/hr + LCU | $0.0225/hr + NLCU | $0.0125/hr + GLCU |
| Best for | Web apps, APIs, microservices | Gaming, IoT, financial trading | Firewalls, IDS/IPS appliances |
ALB — Path-Based and Host-Based Routing
ALB is the workhorse for web applications. It can route requests to different target groups based on the URL path, hostname, HTTP headers, or query strings:
# Create an Application Load Balancer
ALB_ARN=$(aws elbv2 create-load-balancer \
--name web-alb \
--type application \
--subnets subnet-abc123 subnet-def456 \
--security-groups sg-web-alb \
--scheme internet-facing \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
# Create target groups for different services
API_TG=$(aws elbv2 create-target-group \
--name api-targets \
--protocol HTTP --port 8080 \
--vpc-id vpc-abc123 \
--target-type instance \
--health-check-path /health \
--health-check-interval-seconds 15 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 3 \
--query 'TargetGroups[0].TargetGroupArn' --output text)
WEB_TG=$(aws elbv2 create-target-group \
--name web-targets \
--protocol HTTP --port 3000 \
--vpc-id vpc-abc123 \
--target-type instance \
--health-check-path / \
--query 'TargetGroups[0].TargetGroupArn' --output text)
# Register instances to target groups
aws elbv2 register-targets \
--target-group-arn $API_TG \
--targets Id=i-0abc123 Id=i-0def456
aws elbv2 register-targets \
--target-group-arn $WEB_TG \
--targets Id=i-0ghi789 Id=i-0jkl012
Listener Rules for Routing
# Create an HTTPS listener with default action
LISTENER_ARN=$(aws elbv2 create-listener \
--load-balancer-arn $ALB_ARN \
--protocol HTTPS --port 443 \
--certificates CertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/abc-123 \
--default-actions Type=forward,TargetGroupArn=$WEB_TG \
--query 'Listeners[0].ListenerArn' --output text)
# Route /api/* to the API target group (path-based)
aws elbv2 create-rule \
--listener-arn $LISTENER_ARN \
--priority 10 \
--conditions '[{
"Field": "path-pattern",
"Values": ["/api/*"]
}]' \
--actions "[{\"Type\": \"forward\", \"TargetGroupArn\": \"$API_TG\"}]"
# Route api.example.com to the API target group (host-based)
aws elbv2 create-rule \
--listener-arn $LISTENER_ARN \
--priority 20 \
--conditions '[{
"Field": "host-header",
"Values": ["api.example.com"]
}]' \
--actions "[{\"Type\": \"forward\", \"TargetGroupArn\": \"$API_TG\"}]"
# HTTP to HTTPS redirect
aws elbv2 create-listener \
--load-balancer-arn $ALB_ARN \
--protocol HTTP --port 80 \
--default-actions '[{
"Type": "redirect",
"RedirectConfig": {
"Protocol": "HTTPS",
"Port": "443",
"StatusCode": "HTTP_301"
}
}]'
NLB — Ultra-Low Latency with Static IPs
NLB handles millions of requests per second with sub-millisecond latency. It gets a static IP per AZ — critical for whitelisting and DNS:
# Create a Network Load Balancer
NLB_ARN=$(aws elbv2 create-load-balancer \
--name tcp-nlb \
--type network \
--subnets subnet-abc123 subnet-def456 \
--scheme internet-facing \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
# Create a TCP target group
TCP_TG=$(aws elbv2 create-target-group \
--name tcp-targets \
--protocol TCP --port 9092 \
--vpc-id vpc-abc123 \
--target-type instance \
--health-check-protocol TCP \
--health-check-interval-seconds 10 \
--query 'TargetGroups[0].TargetGroupArn' --output text)
# Create a TCP listener
aws elbv2 create-listener \
--load-balancer-arn $NLB_ARN \
--protocol TCP --port 9092 \
--default-actions Type=forward,TargetGroupArn=$TCP_TG
Target Group Types
ALB and NLB support three target types:
# Instance targets (route by instance ID)
aws elbv2 create-target-group \
--name instance-targets \
--target-type instance \
--protocol HTTP --port 80 \
--vpc-id vpc-abc123
# IP targets (route to specific IPs — works cross-VPC)
aws elbv2 create-target-group \
--name ip-targets \
--target-type ip \
--protocol HTTP --port 8080 \
--vpc-id vpc-abc123
# Lambda targets (ALB only — invoke a function per request)
aws elbv2 create-target-group \
--name lambda-targets \
--target-type lambda
aws elbv2 register-targets \
--target-group-arn $LAMBDA_TG \
--targets Id=arn:aws:lambda:us-east-1:123456789012:function:web-handler
Health Checks
Health checks determine which targets receive traffic. Unhealthy targets are automatically removed:
# Configure detailed health checks
aws elbv2 modify-target-group \
--target-group-arn $API_TG \
--health-check-protocol HTTP \
--health-check-path /health \
--health-check-interval-seconds 15 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 3 \
--matcher '{"HttpCode": "200-299"}'
# Check target health status
aws elbv2 describe-target-health \
--target-group-arn $API_TG \
--query 'TargetHealthDescriptions[].{Target:Target.Id,Port:Target.Port,State:TargetHealth.State,Reason:TargetHealth.Reason}' \
--output table
SSL/TLS Termination
ALB decrypts HTTPS at the load balancer, so your backend servers handle plain HTTP:
# Add an ACM certificate to the listener
aws elbv2 add-listener-certificates \
--listener-arn $LISTENER_ARN \
--certificates CertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/wildcard-cert
# Set the SSL policy (TLS 1.2 minimum)
aws elbv2 modify-listener \
--listener-arn $LISTENER_ARN \
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06
Sticky Sessions
Sticky sessions ensure a user's requests always go to the same target. Useful for stateful applications:
# Enable application-based stickiness
aws elbv2 modify-target-group-attributes \
--target-group-arn $WEB_TG \
--attributes \
Key=stickiness.enabled,Value=true \
Key=stickiness.type,Value=app_cookie \
Key=stickiness.app_cookie.cookie_name,Value=SESSIONID \
Key=stickiness.app_cookie.duration_seconds,Value=3600
Warning: Sticky sessions reduce the effectiveness of load balancing. If one target handles many sticky users, it gets overloaded while others are idle. Prefer stateless architectures with external session stores (ElastiCache, DynamoDB) when possible.
Access Logs and Cross-Zone Load Balancing
# Enable access logs to S3
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn $ALB_ARN \
--attributes \
Key=access_logs.s3.enabled,Value=true \
Key=access_logs.s3.bucket,Value=my-alb-logs-bucket \
Key=access_logs.s3.prefix,Value=web-alb
# Enable cross-zone load balancing (ALB: always on, NLB: optional)
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn $NLB_ARN \
--attributes Key=load_balancing.cross_zone.enabled,Value=true
Cross-zone load balancing distributes traffic evenly across all targets in all AZs. Without it, each AZ gets an equal share of traffic regardless of how many targets are in it — causing uneven load if AZs have different numbers of instances.
What's Next?
Load balancers distribute traffic, but what happens when traffic suddenly doubles? You need more servers — automatically. Next, we'll explore Auto Scaling — how to handle traffic spikes, scale down during quiet hours, and mix on-demand with spot instances to cut costs.
This is Part 14 of our AWS series. ALB for HTTP, NLB for TCP, GWLB for appliances. If you remember nothing else, remember that.
