Load balancers: ALB vs NLB — choosing the right entry point
Every production system needs a way to distribute traffic across multiple servers, but not all load balancers are created equal. Over five years building platforms that handle millions of requests, I've seen teams pick the wrong load balancer and end up refactoring months later. The choice between ALB and NLB isn't just a technical detail—it shapes how your infrastructure scales, routes requests, and handles edge cases.
Why this decision matters
When I started my first major project at an e-commerce platform, we threw everything behind an ALB without thinking much about it. It worked fine for a while, but then we integrated real-time inventory updates and live auction functionality. The ALB's layer 7 inspection overhead became noticeable at peak load. That's when I learned that understanding the fundamental differences between these two load balancers could have saved us weeks of debugging.
AWS gives you two main choices for distributing traffic: the Application Load Balancer (ALB) and the Network Load Balancer (NLB). Both sit at the edge of your infrastructure, but they operate at different network layers and excel in different scenarios. Picking correctly means better latency, simpler routing rules, lower costs, and fewer surprises in production.
Comparison — understanding the core differences
The primary distinction comes down to where each load balancer operates in the OSI model:
- ALB operates at Layer 7 (Application Layer). It understands HTTP and HTTPS natively. It can read HTTP headers, hostnames, paths, and even some query parameters to make routing decisions.
- NLB operates at Layer 4 (Transport Layer). It works with TCP and UDP directly. It doesn't care about HTTP semantics—it just moves bytes as fast as possible.
This difference creates a clear trade-off. ALB gives you intelligent routing but requires inspecting every packet. NLB offers ultra-low latency and can handle any protocol, but you get basic routing: port-based or IP-based only.
| Feature | ALB | NLB |
|---|---|---|
| Layer | Layer 7 (HTTP/HTTPS) | Layer 4 (TCP/UDP) |
| Path-based routing | Yes | No |
| Host-based routing | Yes | No |
| Header-based routing | Yes | No |
| Ultra-low latency | Moderate | Yes (sub-millisecond) |
| WebSocket support | Yes (native) | Yes (passthrough) |
| Static IP | No (Elastic IP available) | Yes (native) |
| Cost | Lower | Higher |
I used both on a ride-hailing platform. The ALB fronted our REST API and web portal. The NLB sat in front of our gRPC services because they needed consistent IPs for client certificates and couldn't afford the milliseconds of ALB inspection overhead.
ALB for web applications and microservices
The ALB is the right choice for almost every web application. If you're building REST APIs, GraphQL endpoints, or traditional web services with HTTP/HTTPS, start with ALB.
Routing rules that scale with your architecture. ALB's superpowers come from its routing. You can define rules based on hostname, path, HTTP headers, source IP, or even custom HTTP header values. This means one ALB can serve multiple services without needing a separate load balancer for each.
Here's a realistic routing setup I've deployed on multiple projects:
# Using AWS CLI to create ALB listener rules
# Route /api/* to API backend
# Route /admin/* to admin service
# Route api.example.com to different target group
# Route *.cdn.example.com to CDN originIn Terraform, this translates to clean listener rules that evolve as your architecture grows:
resource "aws_lb_listener_rule" "api_routes" {
listener_arn = aws_lb_listener.http.arn
priority = 1
action {
type = "forward"
target_group_arn = aws_lb_target_group.api.arn
}
condition {
path_pattern {
values = ["/api/*"]
}
}
}
resource "aws_lb_listener_rule" "admin_routes" {
listener_arn = aws_lb_listener.http.arn
priority = 2
action {
type = "forward"
target_group_arn = aws_lb_target_group.admin.arn
}
condition {
path_pattern {
values = ["/admin/*"]
}
}
}TLS termination and certificate management. ALB terminates TLS connections, meaning your backend services can run plain HTTP. This simplifies certificates—you only manage them at the load balancer layer. You can also use wildcard certificates and SNI (Server Name Indication) to serve multiple domains from one ALB.
WAF integration. If you need to block malicious traffic, DDoS protection, or rate limiting, ALB integrates seamlessly with AWS WAF. I've used this on every public-facing service to catch bot traffic and SQL injection attempts before they reach the application layer.
WebSocket support. ALB supports WebSockets natively. On a real-time trading platform, we used ALB with sticky sessions to maintain WebSocket connections to the same backend instance. The ALB handles the HTTP upgrade transparently.
NLB for raw TCP, extreme performance, and fixed IPs
The NLB shines when HTTP semantics don't matter or when you need extreme performance and fixed IPs.
Databases and gRPC passthrough. If you're exposing a database load balancer, gRPC service, or custom protocol, use NLB. It passes traffic through without inspection. On a trading platform, we fronted our market data gRPC services with an NLB to avoid ALB's layer 7 overhead. The latency difference was measurable—roughly 2-5ms saved per request when handling high-frequency updates.
Ultra-low latency requirements. NLB is purpose-built for performance. It uses flow hashing to maintain connection affinity without sticky sessions. If you're building low-latency systems—financial trading, gaming, real-time analytics—NLB is worth the extra cost.
Static IP addresses. NLB supports Elastic IPs natively. This is crucial when clients need to whitelist your load balancer IP or when you're integrating with systems that require fixed IPs. On a B2B SaaS platform, enterprise customers required our API endpoint to have a static IP for their firewall rules. ALB doesn't support this; NLB solved it immediately.
Any protocol support. NLB doesn't care what protocol you send through it. TCP, UDP, custom binary protocols—it all works. I've used this for exposing custom game servers, VPN endpoints, and non-HTTP microservices.
Health checks matter — a bad health check path causes flapping targets
This is where I see teams make costly mistakes. A poorly configured health check can cause your targets to constantly flip between healthy and unhealthy, creating cascading failures.
I learned this the hard way on an e-commerce platform. We set the health check path to /api/health, which was correct. But during traffic spikes, our database connection pool would saturate, causing the health check to timeout. The load balancer would mark instances as unhealthy and route all traffic to remaining instances, overloading them further. This created a death spiral.
Here's what a robust health check looks like:
resource "aws_lb_target_group" "api" {
name = "api-tg"
port = 3000
protocol = "HTTP"
vpc_id = aws_vpc.main.id
health_check {
healthy_threshold = 2
unhealthy_threshold = 3
timeout = 5
interval = 30
path = "/health"
matcher = "200"
}
deregistration_delay = 30
}Key principles I follow:
- Keep health checks lightweight. Your
/healthendpoint should return immediately without querying the database. It should only verify that the process is running and can bind ports. - Set realistic timeouts. A 5-second timeout is standard, but in high-latency environments or during deployments, be generous. I've seen health check timeouts cause false failures on instances that were actually healthy.
- Use appropriate intervals. 30 seconds is the AWS default. For critical systems, I've used 10 seconds. For less critical services, 60 seconds saves API calls.
- Require multiple healthy checks. Set
healthy_thresholdto 2 or 3 so a single spike doesn't trigger deregistration. This prevents flapping. - Graceful draining. Use
deregistration_delay(connection draining) to let in-flight requests complete before removing an instance from the load balancer.
On a SaaS platform handling customer churn prediction, we implemented a health check that verified connectivity to Redis and PostgreSQL. This caught infrastructure problems immediately, but we had to be careful not to make it too strict—we allowed one database to be temporarily unavailable without marking the instance unhealthy.
Common mistakes I see in the field
Choosing NLB for HTTP services. The cost difference ($16/month for ALB vs $32/month for NLB in many regions) adds up. People sometimes pick NLB thinking they need the performance, then discover they need host-based routing. Reverse it if needed, but start with ALB for web services.
Ignoring sticky sessions. On a CRM platform, we didn't enable ALB's sticky sessions for a session-aware service. Users got logged out randomly as requests bounced across instances. Sticky sessions fixed it, but the embarrassment was costly.
Over-engineering health checks. I've seen health checks that query the entire database to verify "system health." This becomes a bottleneck itself. Keep it simple—just verify the service is running.
Forgetting to test failover. Health checks only matter if you actually test them. I've deployed systems where an instance could crash silently and the health check would never notice because we hadn't actually validated the check worked.
Not monitoring target group metrics. Set up CloudWatch alarms for unhealthy target count and request count per target. On a travel platform, we caught a memory leak because one instance was suddenly receiving 10x more traffic than others—a sign it was the only healthy target left.
Key takeaways
- Use ALB for HTTP/HTTPS web applications, REST APIs, and microservices. It costs less and gives you intelligent path, host, and header-based routing.
- Use NLB for non-HTTP protocols, extreme performance requirements, databases, or when you need static IPs. The extra cost is justified for specific use cases.
- Health checks are critical—keep them lightweight, set realistic thresholds, and actually test failover scenarios before production.
- ALB supports WebSockets natively and integrates with WAF. NLB passes any protocol through and supports Elastic IPs.
- Plan for growth. A misconfigured load balancer that works fine at 100 RPS might fail catastrophically at 10,000 RPS. Test under realistic load.
- Monitor your load balancer. CloudWatch metrics like healthy/unhealthy target count, request count, and latency reveal problems before customers see them.






Comments
Loading comments…