Edge & Routing

ALB / NLB

Load balancing for HTTP/HTTPS and TCP/UDP traffic

ALB vs NLB

(Elastic Load Balancing)

ALB (Application Load Balancer) — L7 ingress

Best for: HTTP/HTTPS APIs on ECS/EKS/EC2; routing by host/path/headers, gRPC/HTTP2, blue/green, canary via rules. ([Amazon Web Services, Inc.][7])

Key knobs

  • Listeners: HTTPS + ACM certs, redirects (80→443)
  • Target groups: instance/ip/lambda; health checks (path, matcher)
  • Routing rules: host/path-based, weighted forward
  • Access logs to S3
  • Idle timeout (long-running requests / streaming)
  • Cross-zone and stickiness (sparingly)

Cost mental model

  • Pay per ALB-hour + per LCU-hour (LCU driven by max of: new conns, active conns, bytes, rule evals). ([Amazon Web Services, Inc.][8])
  • Back-of-envelope (us-east-1 style): ~$16/month base + ~$6/month per steady 1 LCU“ALB is ~$20–30/month for light steady traffic; then scales with throughput.” ([Amazon Web Services, Inc.][8])

NLB (Network Load Balancer) — L4 ingress

Best for: TCP/UDP/TLS, very high throughput, static IPs, source IP preservation, lower overhead. (Also supports TLS termination.) ([Amazon Web Services, Inc.][9])

Key knobs

  • Listener protocol: TCP/UDP/TLS
  • Target groups: instance/ip/alb
  • Cross-zone LB (on/off)
  • Proxy protocol / source IP needs (application-level implications)

Cost mental model

  • Pay per NLB-hour + per NLCU-hour (max of: new conns/flows, active, bytes; protocol affects NLCU math). ([Amazon Web Services, Inc.][8])
  • Similar base hourly feel as ALB; scales on network load via NLCU.

ALB vs NLB: decision heuristics (fast)

  • Need HTTP routing, WAF at L7, gRPC, path/host rules → ALB
  • Need TCP/UDP, static IP, extreme throughput, preserve client IP → NLB
  • For most app APIs on ECS/EKS: ALB default; use NLB when you have a clear L4 requirement.

Terraform template (ALB + TG + HTTPS listener + access logs)

# alb.tf
resource "aws_security_group" "alb" {
  name        = "${var.name}-alb-sg"
  description = "ALB ingress"
  vpc_id      = var.vpc_id

  ingress { from_port = 443, to_port = 443, protocol = "tcp", cidr_blocks = ["0.0.0.0/0"] }
  ingress { from_port = 80,  to_port = 80,  protocol = "tcp", cidr_blocks = ["0.0.0.0/0"] }
  egress  { from_port = 0,   to_port = 0,   protocol = "-1",  cidr_blocks = ["0.0.0.0/0"] }
}

resource "aws_lb" "alb" {
  name               = "${var.name}-alb"
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = var.public_subnet_ids

  access_logs {
    bucket  = var.alb_logs_bucket
    prefix  = "alb"
    enabled = true
  }
}

resource "aws_lb_target_group" "app" {
  name        = "${var.name}-tg"
  port        = var.target_port
  protocol    = "HTTP"
  vpc_id      = var.vpc_id
  target_type = "ip" # common for ECS/Fargate

  health_check {
    path                = "/health"
    matcher             = "200-399"
    interval            = 30
    timeout             = 5
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
}

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.alb.arn
  port              = 443
  protocol          = "HTTPS"
  certificate_arn   = var.acm_cert_arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.app.arn
  }
}

resource "aws_lb_listener" "http_redirect" {
  load_balancer_arn = aws_lb.alb.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type = "redirect"
    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

variable "name"              { type = string }
variable "vpc_id"            { type = string }
variable "public_subnet_ids" { type = list(string) }
variable "target_port"       { type = number }
variable "acm_cert_arn"      { type = string }
variable "alb_logs_bucket"   { type = string }

Terraform template (NLB + TG + TLS listener)

# nlb.tf
resource "aws_lb" "nlb" {
  name               = "${var.name}-nlb"
  load_balancer_type = "network"
  subnets            = var.public_subnet_ids
  # For static IPs, allocate EIPs per subnet and use subnet_mapping blocks.
}

resource "aws_lb_target_group" "tcp" {
  name        = "${var.name}-nlb-tg"
  port        = var.target_port
  protocol    = "TCP"
  vpc_id      = var.vpc_id
  target_type = "ip"

  health_check {
    protocol = "TCP"
  }
}

resource "aws_lb_listener" "tls" {
  load_balancer_arn = aws_lb.nlb.arn
  port              = 443
  protocol          = "TLS"
  certificate_arn   = var.acm_cert_arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.tcp.arn
  }
}

variable "name"              { type = string }
variable "vpc_id"            { type = string }
variable "public_subnet_ids" { type = list(string) }
variable "target_port"       { type = number }
variable "acm_cert_arn"      { type = string }