Load Balancers and Gateways
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. See the section bibliography for the reference material consulted while preparing these pages. |
Commands and behaviour on this page are current for AWS CLI v2 (2.37 line) and the Elastic Load Balancing API
(aws elbv2), including the Application Load Balancer JWT verification and URL and host-header rewrite features, and for
the AWS Load Balancer Controller 3.x Gateway API support (all checked 2026-10-01); re-verify against aws --version and
the linked reference pages before relying on them in a pipeline. Examples use us-east-1 and the placeholder account ID
111122223333; both are stand-ins for your own values.
Every request that reaches Bookshelf has to land on a healthy task, container or function, over TLS, without a single
machine being a single point of failure. On AWS that job is shared by a family of services: the Elastic Load Balancing
(ELB) load balancers for HTTP, TCP and appliance traffic, AWS Global Accelerator for a global anycast front door,
Amazon VPC Lattice for service-to-service traffic, and NAT and internet gateways for traffic that leaves the VPC. This
page explains what each one is for, how to pick between them, and walks through creating the Bookshelf alb-bookshelf
Application Load Balancer end to end, from target group to HTTPS listener, routing rules and AWS WAF. It then covers the
Network Load Balancer, the Gateway Load Balancer, Global Accelerator, VPC Lattice and the Kubernetes Gateway API on EKS,
and finishes with IAM, pricing, limits, troubleshooting and clean-up.
The API gateway as an architecture pattern (when to have one, backends for frontends, cross-cutting concerns) is discussed in API Gateway and Backends for Frontends and is not repeated here; the managed AWS product for that pattern is API Gateway. The VPC itself, subnets and security groups are in VPC Networking. For the Azure counterparts (Application Gateway, Azure Load Balancer, Front Door) see Gateways and Load Balancing.
export AWS_REGION=us-east-1 # placeholder Region
export ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export VPC_ID=vpc-0123456789abcdef0 # placeholder; the Bookshelf VPC
export PUBLIC_SUBNETS="subnet-aaaa1111 subnet-bbbb2222" # placeholders; one per Availability Zone
The load balancing landscape
Elastic Load Balancing offers four load balancer types. The first three are current; the fourth is legacy.
| Type | Layer and protocols | Typical use | Notes |
|---|---|---|---|
Application Load Balancer (ALB) |
Layer 7: HTTP, HTTPS, HTTP/2, gRPC, WebSocket. |
Web applications and HTTP APIs; path, host, header and query routing; ECS, EKS, EC2 and Lambda targets. |
Terminates TLS, integrates with AWS WAF, can authenticate users (OIDC, Amazon Cognito) and validate JWTs. |
Network Load Balancer (NLB) |
Layer 4: TCP, UDP, TLS, QUIC passthrough. |
Non-HTTP protocols, very high connection rates, static IP addresses, PrivateLink services, preserving the client IP. |
One static IP per enabled subnet (optionally an Elastic IP); can have a security group; can front an ALB. |
Gateway Load Balancer (GWLB) |
Layer 3 gateway plus load balancer: GENEVE on port 6081. |
Inserting third-party virtual appliances (firewalls, intrusion prevention, deep packet inspection) transparently. |
Used with Gateway Load Balancer endpoints; usually consumed through a security-vendor solution. |
Classic Load Balancer (CLB) |
Layer 4 and basic layer 7 on EC2-Classic-era design. |
Existing legacy workloads only. |
Legacy. Older guides still show it; new work should use an ALB or NLB. |
Around them sit services that are not load balancers in the strict sense but solve neighbouring problems:
-
AWS Global Accelerator gives your application two static anycast IP addresses and routes users over the AWS global network to the closest healthy regional endpoint (ALB, NLB, EC2 instance or Elastic IP).
-
Amazon CloudFront is the Layer 7 global edge cache and is covered on CloudFront and CDN Delivery; it is often placed in front of an ALB.
-
Amazon API Gateway is the managed API front door with authorizers, throttling and usage plans; see API Gateway.
-
Amazon VPC Lattice is a managed application-networking layer for service-to-service traffic across VPCs and accounts, with IAM-based auth policies.
-
NAT gateways and internet gateways carry outbound traffic from private subnets and public traffic into public subnets; they are recapped at the end of this page and detailed on VPC Networking.
Decision tree
The first questions are always the same: is the traffic HTTP, do you need a global rather than regional entry point, and are you balancing north-south (client to service) or east-west (service to service) traffic?
On Kubernetes the same choices apply, but you ask for them through the AWS Load Balancer Controller: an Ingress or a
Gateway API Gateway with HTTPRoute becomes an ALB, and a Service of type LoadBalancer (or a TCPRoute) becomes
an NLB. See The Gateway API and the AWS Load Balancer Controller on EKS.
Application Load Balancer
An Application Load Balancer is made of four things you configure separately and then wire together:
- Load balancer
-
The regional resource: a scheme (
internet-facingorinternal), two or more subnets in different Availability Zones, security groups and attributes. The ALB creates one node per enabled Availability Zone. - Listener
-
A protocol and port the ALB listens on (
HTTP:80,HTTPS:443). An HTTPS listener has a certificate and a TLS security policy. - Rule
-
Each listener has a default rule plus numbered rules. A rule has a priority, one or more conditions (path, host header, HTTP header, method, query string, source IP) and an action:
forward,redirect,fixed-response, or, on HTTPS listeners,authenticate-oidc,authenticate-cognitoorjwt-validationfollowed by a forward. - Target group
-
The set of destinations and their health check. Target types are
instance,ip(ECS tasks withawsvpc, EKS pods, on-premises addresses over VPN or Direct Connect),lambdaandalb(for an NLB in front of an ALB). One ALB can route to many target groups, so one ALB can frontcatalog-apiandorders-apiwith different path or host rules.
The Bookshelf layout, as used by the ECS pages, is one ALB in the two public subnets with an HTTPS listener whose rules
send /catalog/ to the catalog-api target group and /orders/ to the orders-api target group, and tasks in
private subnets that accept traffic only from the ALB security group.
In the AWS Management Console
-
Open the EC2 console, make sure the Region selector (top right) shows your Region, and choose Load Balancers in the navigation pane (under Load Balancing), then Create load balancer.
-
Under Application Load Balancer choose Create.
-
Basic configuration: name
alb-bookshelf, SchemeInternet-facing, IP address typeIPv4(orDualstack). -
Network mapping: choose the Bookshelf VPC and select at least two Availability Zones, picking the public subnet in each.
-
Security groups: choose the ALB security group that allows inbound 80 and 443 from the internet (see Networking and security groups).
-
Listeners and routing: add an
HTTPS:443listener and, under Default action, choose Create target group. A new tab opens: choose IP addresses as the target type, name ittg-catalog-api, protocolHTTP, port8080, the Bookshelf VPC, and set the Health check path to/actuator/health/readiness. Skip registering targets (ECS or EKS registers them) and Create target group. -
Back on the load balancer page refresh the target group list and select
tg-catalog-api. Under Secure listener settings pick a Security policy and a certificate from AWS Certificate Manager (ACM). -
Optionally enable AWS WAF under Optimize with service integrations and choose a web ACL (Region scope).
-
Create load balancer. When State becomes
Active, open the listener, choose Add rule and add the path rules and the HTTP-to-HTTPS redirect described below.
With the AWS CLI
# Security group for the ALB: HTTP and HTTPS from anywhere
SG_ALB=$(aws ec2 create-security-group --group-name sg-alb-bookshelf \
--description "alb-bookshelf" --vpc-id "$VPC_ID" --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG_ALB" --protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id "$SG_ALB" --protocol tcp --port 443 --cidr 0.0.0.0/0
# The load balancer
ALB_ARN=$(aws elbv2 create-load-balancer --name alb-bookshelf --type application \
--scheme internet-facing --ip-address-type ipv4 \
--subnets $PUBLIC_SUBNETS --security-groups "$SG_ALB" \
--tags Key=app,Value=bookshelf \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
# Wait until it is active, then read its DNS name
aws elbv2 wait load-balancer-available --load-balancer-arns "$ALB_ARN"
aws elbv2 describe-load-balancers --load-balancer-arns "$ALB_ARN" \
--query 'LoadBalancers[0].[DNSName,State.Code]' --output text
# Target groups, one per service (IP targets for awsvpc tasks and pods)
TG_CATALOG=$(aws elbv2 create-target-group --name tg-catalog-api --protocol HTTP --port 8080 \
--target-type ip --vpc-id "$VPC_ID" \
--health-check-path /actuator/health/readiness --health-check-interval-seconds 15 \
--healthy-threshold-count 2 --unhealthy-threshold-count 3 --matcher HttpCode=200 \
--query 'TargetGroups[0].TargetGroupArn' --output text)
TG_ORDERS=$(aws elbv2 create-target-group --name tg-orders-api --protocol HTTP --port 8080 \
--target-type ip --vpc-id "$VPC_ID" --health-check-path /actuator/health/readiness \
--query 'TargetGroups[0].TargetGroupArn' --output text)
See create-load-balancer, create-target-group and the Create an Application Load Balancer guide.
Listeners, rules and redirects
A listener with a default action is enough to serve traffic; rules then specialise it. Create the HTTPS listener first,
with a certificate from ACM (a public certificate with DNS validation is requested in Route 53, DNS and ACM Certificates; certificates for a regional load balancer
must be in the load balancer’s own Region, unlike CloudFront which needs us-east-1).
export CERT_ARN="arn:aws:acm:${AWS_REGION}:${ACCOUNT_ID}:certificate/EXAMPLE-CERT-ID" # placeholder
# HTTPS listener: a 404 fixed response by default, so only explicit rules reach a service
HTTPS_ARN=$(aws elbv2 create-listener --load-balancer-arn "$ALB_ARN" \
--protocol HTTPS --port 443 \
--certificates CertificateArn="$CERT_ARN" \
--ssl-policy ELBSecurityPolicy-TLS13-1-2-Res-2021-06 \
--default-actions 'Type=fixed-response,FixedResponseConfig={StatusCode=404,ContentType=text/plain,MessageBody=not found}' \
--query 'Listeners[0].ListenerArn' --output text)
# HTTP listener: permanent redirect to HTTPS, keeping host, path and query
aws elbv2 create-listener --load-balancer-arn "$ALB_ARN" --protocol HTTP --port 80 \
--default-actions '[{
"Type": "redirect",
"RedirectConfig": {
"Protocol": "HTTPS", "Port": "443",
"Host": "#{host}", "Path": "/#{path}", "Query": "#{query}",
"StatusCode": "HTTP_301"
}
}]'
# Path rules: /catalog/* -> catalog-api, /orders/* -> orders-api
aws elbv2 create-rule --listener-arn "$HTTPS_ARN" --priority 10 \
--conditions '[{"Field":"path-pattern","PathPatternConfig":{"Values":["/catalog/*"]}}]' \
--actions "Type=forward,TargetGroupArn=$TG_CATALOG"
aws elbv2 create-rule --listener-arn "$HTTPS_ARN" --priority 20 \
--conditions '[{"Field":"path-pattern","PathPatternConfig":{"Values":["/orders/*"]}}]' \
--actions "Type=forward,TargetGroupArn=$TG_ORDERS"
# Host-based rule combined with a header condition
aws elbv2 create-rule --listener-arn "$HTTPS_ARN" --priority 30 \
--conditions '[
{"Field":"host-header","HostHeaderConfig":{"Values":["admin.bookshelf.example.com"]}},
{"Field":"source-ip","SourceIpConfig":{"Values":["203.0.113.0/24"]}}
]' \
--actions "Type=forward,TargetGroupArn=$TG_ORDERS"
The HTTP-to-HTTPS redirect is the standard way to enforce TLS. Rule priorities run from 1 to 50,000; the lowest number
that matches wins, so leave gaps (10, 20, 30) to insert rules later. The redirect keywords {host}, {path},
{port}, {protocol} and #{query} are replaced by the ALB with parts of the original URL.
Weighted target groups for canary releases
A forward action can list several target groups with weights from 0 to 999. A common use is a canary release between a
stable and a new target group; there is no automatic fail-over to the healthy group if one group has no healthy targets.
Enable target group stickiness so a client keeps seeing the same version.
aws elbv2 modify-rule --rule-arn "$RULE_CATALOG_ARN" --actions '[{
"Type": "forward",
"ForwardConfig": {
"TargetGroups": [
{"TargetGroupArn": "'"$TG_CATALOG"'", "Weight": 95},
{"TargetGroupArn": "'"$TG_CATALOG_V2"'", "Weight": 5}
],
"TargetGroupStickinessConfig": {"Enabled": true, "DurationSeconds": 600}
}
}]'
See modify-rule. ECS built-in blue/green and canary deployments drive this mechanism for you; see ECS Deployments, Scaling and Networking.
Authentication, JWT verification and rewrites
An HTTPS listener rule can do more than route:
-
User authentication with
authenticate-oidc(any OpenID Connect provider) orauthenticate-cognito(an Amazon Cognito user pool) redirects unauthenticated browsers to the identity provider and passes verified claims to the target inx-amzn-oidc-*headers. It suits internal tools and back-office pages, not public APIs. -
JWT verification with the
jwt-validationaction validates access tokens on API traffic at the ALB: you supply the issuer and the JWKS endpoint plus optional required claims, and requests with a missing or invalid token are rejected before they reach the service. This removes token-checking code from simple services; complex authorization stays in the application. See OAuth Reference for the underlying OAuth and OIDC concepts. -
Transforms (
url-rewrite,host-header-rewrite) rewrite the request path or the Host header with a regular expression before forwarding, so a public path such as/catalog/*can reach a service that expects/.
# Rewrite /catalog/books -> /books before forwarding to the catalog-api service
aws elbv2 modify-rule --rule-arn "$RULE_CATALOG_ARN" \
--transforms '[{
"Type": "url-rewrite",
"UrlRewriteConfig": {"Rewrites": [{"Regex": "^/catalog/(.*)$", "Replace": "/$1"}]}
}]'
# Require a valid JWT from the Bookshelf identity provider on the orders rule (placeholders)
aws elbv2 modify-rule --rule-arn "$RULE_ORDERS_ARN" --actions '[
{"Type": "jwt-validation", "Order": 1,
"JwtValidationConfig": {
"Issuer": "https://login.example.com/",
"JwksEndpoint": "https://login.example.com/.well-known/jwks.json"}},
{"Type": "forward", "Order": 2, "TargetGroupArn": "'"$TG_ORDERS"'"}
]'
See JWT verification, Authenticate users using an Application Load Balancer and Transforms. Both features are recent; confirm the exact syntax and Regional availability in the guides before relying on them.
Health checks
A load balancer sends traffic only to targets that pass the health check of the target group they are in. The settings that matter:
-
Protocol, port and path. An HTTP check against a readiness endpoint such as the Spring Boot Actuator path
/actuator/health/readiness; use the traffic port unless the application exposes management on another one. -
Interval and thresholds. The default interval is 30 seconds with five healthy and two unhealthy checks; a 10 to 15 second interval with a healthy threshold of 2 brings a new task into service faster. The
timeoutmust be shorter than the interval. -
Success codes (matcher).
200by default; widen to a range such as200-399only if the endpoint redirects. gRPC target groups use gRPC status codes. -
Slow start.
slow_start.duration_secondsramps traffic into a new target so a cold JVM is not hit with a full share. -
Deregistration delay.
deregistration_delay.timeout_seconds(default 300) keeps in-flight requests going to a target that is being removed; for fast ECS deployments set it near the container stop timeout, not 300 seconds. -
Routing algorithm. The default is round robin; set
load_balancing.algorithm.type=least_outstanding_requestswhen request durations vary a lot.
aws elbv2 modify-target-group-attributes --target-group-arn "$TG_CATALOG" --attributes \
Key=deregistration_delay.timeout_seconds,Value=30 \
Key=slow_start.duration_seconds,Value=30 \
Key=load_balancing.algorithm.type,Value=least_outstanding_requests
# Inspect health, including the reason a target is unhealthy
aws elbv2 describe-target-health --target-group-arn "$TG_CATALOG" \
--query 'TargetHealthDescriptions[].[Target.Id,TargetHealth.State,TargetHealth.Reason,TargetHealth.Description]' \
--output table
See describe-target-health and Health checks for your target groups. If the load balancer has no healthy target in any group it fails open and routes to all targets, so a broken health check endpoint can look like "everything works but errors".
TLS termination
The usual design terminates TLS at the load balancer and runs plain HTTP, or re-encrypted HTTPS, to the targets inside the VPC.
-
Certificates. An HTTPS listener takes one default certificate and, with Server Name Indication (SNI), additional certificates for other domains (
aws elbv2 add-listener-certificates). Use ACM public certificates with DNS validation and managed renewal; the load balancer picks up the renewed certificate automatically. Import a certificate (a self-signed one is acceptable for tests only) when you cannot use ACM. -
Security policy. The
--ssl-policysets the minimum TLS version and cipher suites. Pick a current TLS 1.2 and 1.3 policy such asELBSecurityPolicy-TLS13-1-2-Res-2021-06, or one of the newer policies the console lists, and re-check the list periodically; the older policies remain for compatibility only. -
End-to-end encryption. Create the target group with protocol
HTTPS; the ALB does not verify the target certificate, so a self-signed certificate on the task works but should not be your only identity control. -
Mutual TLS. An ALB listener can verify client certificates against a trust store (
verifyorpassthroughmode), for business-to-business APIs. -
HTTP headers. The ALB adds
X-Forwarded-For,X-Forwarded-ProtoandX-Forwarded-Port; configure Spring Boot withserver.forward-headers-strategy=frameworkso redirects and generated links usehttps. Turn onrouting.http.drop_invalid_header_fields.enabledand keeprouting.http.desync_mitigation_modeat its default (defensive) to harden against request smuggling. -
NLB. A TLS listener on a Network Load Balancer terminates TLS with an ACM certificate (and supports ALPN policies); a TCP listener passes encrypted traffic straight through.
aws elbv2 add-listener-certificates --listener-arn "$HTTPS_ARN" \
--certificates CertificateArn="arn:aws:acm:${AWS_REGION}:${ACCOUNT_ID}:certificate/EXAMPLE-SECOND-CERT"
aws elbv2 modify-load-balancer-attributes --load-balancer-arn "$ALB_ARN" --attributes \
Key=routing.http.drop_invalid_header_fields.enabled,Value=true \
Key=idle_timeout.timeout_seconds,Value=60 \
Key=deletion_protection.enabled,Value=true
Sticky sessions
By default each request is routed independently. Sticky sessions bind a client to one target for a period, by an
ALB-generated cookie (AWSALB, lb_cookie), an application-set cookie (app_cookie), or, on an NLB, by source IP.
They are a workaround for applications that keep session state in memory; the better design is to keep state in a shared
store (see Caching, Graph and Partner Databases) and leave stickiness off. If you do need them, they
are set per target group:
aws elbv2 modify-target-group-attributes --target-group-arn "$TG_CATALOG" --attributes \
Key=stickiness.enabled,Value=true \
Key=stickiness.type,Value=lb_cookie \
Key=stickiness.lb_cookie.duration_seconds,Value=3600
Stickiness limits how evenly traffic is spread and can concentrate load on one task after a scale-out, so combine it with the least-outstanding-requests algorithm and measure. See Sticky sessions for your Application Load Balancer.
AWS WAF integration
AWS WAF is a web application firewall that you attach to an ALB (and to CloudFront, API Gateway and AppSync) through a
web access control list (web ACL). An ALB web ACL has REGIONAL scope and must be in the load balancer’s Region. Start
with AWS managed rule groups (the core rule set, known bad inputs, SQL injection) in count mode, review the sampled
requests and logs, then switch to block. Add a rate-based rule to blunt abusive clients.
cat > waf-rules.json <<'JSON'
[
{
"Name": "AWSManagedCommon",
"Priority": 1,
"Statement": {"ManagedRuleGroupStatement": {"VendorName": "AWS", "Name": "AWSManagedRulesCommonRuleSet"}},
"OverrideAction": {"Count": {}},
"VisibilityConfig": {"SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "AWSManagedCommon"}
},
{
"Name": "RateLimitPerIp",
"Priority": 2,
"Statement": {"RateBasedStatement": {"Limit": 2000, "AggregateKeyType": "IP"}},
"Action": {"Block": {}},
"VisibilityConfig": {"SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "RateLimitPerIp"}
}
]
JSON
WEB_ACL_ARN=$(aws wafv2 create-web-acl --name bookshelf-web-acl --scope REGIONAL \
--default-action Allow={} --rules file://waf-rules.json \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=bookshelf-web-acl \
--query 'Summary.ARN' --output text)
aws wafv2 associate-web-acl --web-acl-arn "$WEB_ACL_ARN" --resource-arn "$ALB_ARN"
See create-web-acl, associate-web-acl and the AWS WAF Developer Guide. If CloudFront sits in front of the ALB, attach the web ACL to CloudFront instead and lock the ALB down so it accepts traffic only from CloudFront; see CloudFront and CDN Delivery. AWS WAF is billed per web ACL, per rule and per million requests.
Network Load Balancer
A Network Load Balancer operates at Layer 4. Choose it when the protocol is not HTTP (databases, MQTT, game servers, syslog, UDP), when you need static addresses that customers can allow-list, when you need to preserve the client source IP, for extreme connection rates and low latency, or as the front of an AWS PrivateLink endpoint service. Features:
-
Protocols. TCP, UDP, TCP_UDP, TLS and QUIC (passthrough, using the connection ID for stickiness).
-
Static addresses. One network interface and static IP per enabled subnet, with an optional Elastic IP per subnet on an internet-facing NLB.
-
Client IP preservation. Depends on target type and protocol; check
preserve_client_ip.enabledon the target group. -
Security groups. An NLB can have security groups attached (set at creation); a load balancer created without one cannot gain one afterwards, and targets then must allow the clients or the NLB addresses as appropriate.
-
Cross-zone. Off by default on an NLB (each node targets only its own zone), so keep targets balanced across zones or enable
load_balancing.cross_zone.enabledand accept the cross-AZ data charge. -
Target types.
instance,ipandalb; weighted target groups are supported on NLB listeners for gradual shifts. -
No HTTP routing. There are no path, host or header rules, and no WAF; put an ALB behind the NLB (target type
alb) if you need both static IPs and HTTP routing.
NLB_ARN=$(aws elbv2 create-load-balancer --name nlb-bookshelf-internal --type network \
--scheme internal --subnets $PUBLIC_SUBNETS --security-groups "$SG_NLB" \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
TG_TCP=$(aws elbv2 create-target-group --name tg-notifier-tcp --protocol TCP --port 9000 \
--target-type ip --vpc-id "$VPC_ID" --health-check-protocol TCP \
--query 'TargetGroups[0].TargetGroupArn' --output text)
# TLS listener terminating with an ACM certificate, HTTP/2 preferred via ALPN
aws elbv2 create-listener --load-balancer-arn "$NLB_ARN" --protocol TLS --port 443 \
--certificates CertificateArn="$CERT_ARN" --ssl-policy ELBSecurityPolicy-TLS13-1-2-Res-2021-06 \
--alpn-policy HTTP2Preferred --default-actions "Type=forward,TargetGroupArn=$TG_TCP"
aws elbv2 modify-target-group-attributes --target-group-arn "$TG_TCP" --attributes \
Key=preserve_client_ip.enabled,Value=true Key=proxy_protocol_v2.enabled,Value=false
In the console the flow is the same as for the ALB: Create load balancer → Network Load Balancer, then a
Listener with protocol TCP, UDP, TLS or QUIC and a target group. See
create-load-balancer and
Create a Network Load
Balancer. To publish the NLB as a private service to other VPCs, create an endpoint service on it
(aws ec2 create-vpc-endpoint-service-configuration --network-load-balancer-arns "$NLB_ARN"), described on
VPC Networking.
Gateway Load Balancer and the Classic Load Balancer
A Gateway Load Balancer combines a transparent network gateway with a load balancer for fleets of virtual appliances
(next-generation firewalls, intrusion detection, deep packet inspection). Traffic is steered to a Gateway Load Balancer
endpoint in your VPC through route tables, encapsulated with GENEVE on port 6081 to the appliance fleet, inspected, and
returned. It is usually deployed from a security partner’s AWS Marketplace listing rather than built by hand, and
Bookshelf does not need one. Its target group uses protocol GENEVE and health checks on the appliance:
GWLB_ARN=$(aws elbv2 create-load-balancer --name gwlb-inspection --type gateway \
--subnets $PUBLIC_SUBNETS --query 'LoadBalancers[0].LoadBalancerArn' --output text)
TG_GENEVE=$(aws elbv2 create-target-group --name tg-appliances --protocol GENEVE --port 6081 \
--vpc-id "$VPC_ID" --health-check-protocol TCP --health-check-port 80 \
--query 'TargetGroups[0].TargetGroupArn' --output text)
aws elbv2 create-listener --load-balancer-arn "$GWLB_ARN" --default-actions "Type=forward,TargetGroupArn=$TG_GENEVE"
See What is a Gateway Load
Balancer?. The Classic Load Balancer (aws elb) is the original 2009 design and is legacy: older guides and
tutorials still show it, and the in-tree Kubernetes cloud provider used to create one for Service objects of type
LoadBalancer. Migrate existing ones to an ALB or NLB with the migration wizard in the console; do not create new ones.
AWS Global Accelerator
Global Accelerator gives your application two static anycast IPv4 addresses (and optionally two IPv6) that are announced from AWS edge locations. Users enter the AWS network at the nearest edge location and are carried over the AWS backbone to the best healthy regional endpoint, which is more consistent than the public internet. Use it for TCP or UDP applications with a global audience, for instant fail-over between Regions without waiting for DNS TTLs, for allow-listed fixed addresses, and for blue/green or traffic-dial shifts between Regions. Its endpoints are ALBs, NLBs, EC2 instances and Elastic IPs.
How it differs from the neighbours: CloudFront caches HTTP content at the edge and terminates HTTP there (see
CloudFront and CDN Delivery); Route 53 routes by DNS answer (see
Route 53, DNS and ACM Certificates); Global Accelerator does neither, it proxies and accelerates traffic
with fixed IPs. The Global Accelerator control-plane API is served from the us-west-2 Region, so CLI calls need
--region us-west-2 regardless of where the endpoints are.
ACC_ARN=$(aws globalaccelerator create-accelerator --name bookshelf --ip-address-type IPV4 \
--region us-west-2 --query 'Accelerator.AcceleratorArn' --output text)
LIS_ARN=$(aws globalaccelerator create-listener --accelerator-arn "$ACC_ARN" --protocol TCP \
--port-ranges FromPort=443,ToPort=443 --region us-west-2 --query 'Listener.ListenerArn' --output text)
aws globalaccelerator create-endpoint-group --listener-arn "$LIS_ARN" --endpoint-group-region "$AWS_REGION" \
--endpoint-configurations EndpointId="$ALB_ARN",Weight=100,ClientIPPreservationEnabled=true \
--health-check-protocol HTTPS --health-check-path /actuator/health/readiness --region us-west-2
In the console open AWS Global Accelerator → Create accelerator, add a listener (ports and protocol), an endpoint group per Region, and the endpoints. See AWS CLI Command Reference: globalaccelerator and the Global Accelerator Developer Guide.
Amazon VPC Lattice
VPC Lattice is the managed option for east-west traffic: instead of building load balancers, peering and security group rules between every pair of services, you publish each service into a service network and associate the VPCs that need to call it. Components:
-
Service network: the logical boundary; VPCs and services are associated with it, and an optional auth policy applies to everything in it.
-
Service: a named application with listeners and rules (HTTP, HTTPS, gRPC, TLS) and an optional custom domain.
-
Target group: instances, IP addresses, Lambda functions, ALBs or Kubernetes pods.
-
Auth policy: an IAM-style resource policy (
AWS_IAMauth) that says which principals may call which service paths, so service-to-service authorization uses SigV4 and IAM roles instead of network location alone. -
Resource gateways and resource configurations: let clients reach non-HTTP resources (a database, an IP or DNS target) across VPCs without peering.
Lattice replaces the sidecar-based AWS App Mesh (which reaches end of support on 2026-09-30) for most new service networking. It does not replace an ALB for client-facing north-south traffic; an internet client reaches a Lattice service only through something that is inside the service network.
SN_ID=$(aws vpc-lattice create-service-network --name bookshelf-net --auth-type AWS_IAM \
--query id --output text)
aws vpc-lattice create-service-network-vpc-association --service-network-identifier "$SN_ID" \
--vpc-identifier "$VPC_ID" --security-group-ids "$SG_LATTICE_CLIENTS"
SVC_ID=$(aws vpc-lattice create-service --name catalog-api --auth-type AWS_IAM --query id --output text)
LTG_ID=$(aws vpc-lattice create-target-group --name catalog-api-tg --type IP \
--config '{"port":8080,"protocol":"HTTP","vpcIdentifier":"'"$VPC_ID"'"}' --query id --output text)
aws vpc-lattice create-listener --service-identifier "$SVC_ID" --name http --protocol HTTP --port 80 \
--default-action '{"forward":{"targetGroups":[{"targetGroupIdentifier":"'"$LTG_ID"'"}]}}'
aws vpc-lattice create-service-network-service-association \
--service-network-identifier "$SN_ID" --service-identifier "$SVC_ID"
See AWS CLI Command Reference: vpc-lattice and
the Amazon VPC Lattice User Guide. On
Kubernetes the AWS Gateway API Controller maps Gateway and HTTPRoute objects to Lattice, covered on
Deploying Microservices on EKS.
The Gateway API and the AWS Load Balancer Controller on EKS
On EKS you rarely call aws elbv2 by hand. The AWS Load Balancer Controller (LBC) watches Kubernetes objects and
creates the matching load balancers: an Ingress becomes an ALB, a Service of type LoadBalancer an NLB, and, in the
v3 line, the Kubernetes Gateway API maps Gateway with HTTPRoute or GRPCRoute to an ALB and TCPRoute, UDPRoute
or TLSRoute to an NLB. The Gateway API is the successor to Ingress: the platform team owns the Gateway (listeners,
certificate, scheme) and each application team owns its HTTPRoute, with weighted backends, header matching and
redirects standardised in the API rather than in annotations. Mixing L7 and L4 routes on one Gateway is not supported.
EKS Auto Mode has built-in ALB and NLB integration for Ingress and Service; for the Gateway API on Auto Mode install
the LBC yourself.
The ELB resources this page creates by hand (listeners, rules, target groups, sticky sessions, WAF, TLS policies) are the
same ones the controller manages; it exposes them as LoadBalancerConfiguration and TargetGroupConfiguration
objects and as Ingress annotations. The installation with Pod Identity, the full Bookshelf Gateway, HTTPRoute
and TargetGroupConfiguration manifests, the ingress-nginx retirement (2026-03-24) and the move from Ingress with
ingress2gateway are on Deploying Microservices on EKS, and the generic Kubernetes background on
Orchestration: Swarm Mode and Kubernetes. Associate an AWS WAF web ACL and a security group with a
controller-managed ALB through the LoadBalancerConfiguration rather than editing the ALB in the console, because the
controller reconciles manual changes away.
Typical layered topologies
| Topology | When it fits | Notes |
|---|---|---|
CloudFront → ALB → ECS or EKS |
Public website or API with static assets and cacheable responses. |
Terminate TLS twice, attach WAF at CloudFront, restrict the ALB to the CloudFront managed prefix list and a secret header. See CloudFront and CDN Delivery and ECS and Fargate. |
CloudFront → API Gateway → Lambda, or API Gateway → VPC link → internal ALB |
APIs that need authorizers, throttling and usage plans in front of containers. |
The ALB is internal; the VPC link is the only way in. See API Gateway. |
Global Accelerator → ALBs in two Regions |
Active-active or active-passive multi-Region service needing static IPs and quick fail-over. |
Health check each Regional ALB; use traffic dials for maintenance. |
NLB → PrivateLink endpoint service |
Offer a private TCP or TLS service to other VPCs or accounts. |
Consumers create an interface endpoint; no peering or overlapping-CIDR problems. |
NLB (static IP) → ALB |
Customers must allow-list fixed IPs but you need Layer 7 routing. |
Use target type |
VPC Lattice service network |
Many services in several VPCs or accounts calling each other with IAM authorization. |
Replaces hand-built peering and per-service internal load balancers. |
Networking and security groups
Load balancers live in the VPC and are protected with security groups; the full VPC picture is in VPC Networking.
-
Subnets. Place an internet-facing load balancer in public subnets in at least two Availability Zones (each subnet needs enough free addresses; AWS recommends at least eight free IPs in a /27 or larger) and the targets in private subnets. An internal load balancer lives in private subnets.
-
Security group chain. The ALB group allows 80 and 443 from the internet (or from the CloudFront prefix list); the task or pod group allows the container port only from the ALB group. Reference groups, not CIDRs, so the rule survives scaling.
-
Health check reachability. Health checks come from the load balancer’s addresses in your subnets, so the target group security rule that allows the traffic port also covers them.
-
Cross-zone load balancing. On by default for an ALB (no charge for inter-AZ data to targets), configurable on an NLB and a GWLB where it incurs standard inter-AZ transfer charges.
-
IPv6. Choose
dualstackfor IPv6 clients; targets stay IPv4 unless the target group is IPv6. -
Outbound. Private targets reach the internet through a NAT gateway in a public subnet (or reach AWS APIs through VPC endpoints); the NAT gateway is billed per hour and per gigabyte processed, so keep it out of the path of heavy traffic such as image pulls. A newer regional NAT gateway mode removes the need for one NAT gateway per Availability Zone; see VPC Networking for NAT gateway choices, costs and the internet gateway.
-
Public IPv4 addresses. Each public IPv4 address, including those on an internet-facing load balancer, is billed per hour.
-
Access logs. Enable access logs to an S3 bucket (and connection logs for mutual TLS) for forensic work.
# Task security group accepts the container port only from the ALB security group
SG_TASKS=$(aws ec2 create-security-group --group-name sg-tasks-bookshelf \
--description "bookshelf tasks" --vpc-id "$VPC_ID" --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG_TASKS" --protocol tcp --port 8080 --source-group "$SG_ALB"
# Access logs to S3 (the bucket policy must allow the regional ELB log delivery principal)
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="bookshelf-alb-logs-${ACCOUNT_ID}" \
Key=access_logs.s3.prefix,Value=alb-bookshelf
Security and IAM
Two sets of permissions matter. People and pipelines need permission to manage load balancers; the load balancer itself needs permission to read certificates and write logs.
An operator who manages the Bookshelf load balancers but may not delete them, and may only touch resources tagged
app=bookshelf:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadElasticLoadBalancing",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:Describe*",
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"acm:ListCertificates",
"acm:DescribeCertificate",
"wafv2:ListWebACLs"
],
"Resource": "*"
},
{
"Sid": "CreateTaggedLoadBalancerResources",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateTargetGroup",
"elasticloadbalancing:AddTags"
],
"Resource": "*",
"Condition": {
"StringEquals": { "aws:RequestTag/app": "bookshelf" }
}
},
{
"Sid": "ManageBookshelfLoadBalancers",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:CreateRule",
"elasticloadbalancing:Modify*",
"elasticloadbalancing:SetSecurityGroups",
"elasticloadbalancing:SetSubnets",
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:DeregisterTargets",
"elasticloadbalancing:AddListenerCertificates",
"elasticloadbalancing:SetWebAcl"
],
"Resource": "*",
"Condition": {
"StringEquals": { "aws:ResourceTag/app": "bookshelf" }
}
},
{
"Sid": "AttachWebAclToLoadBalancer",
"Effect": "Allow",
"Action": [
"wafv2:AssociateWebACL",
"wafv2:DisassociateWebACL",
"wafv2:GetWebACLForResource"
],
"Resource": "*"
},
{
"Sid": "DenyDeletes",
"Effect": "Deny",
"Action": [
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:DeleteTargetGroup"
],
"Resource": "*"
}
]
}
Notes on this policy: a listener and a rule inherit permissions through the load balancer’s tags only for some
actions, so test each action you grant; the Describe* actions do not support resource-level permissions. The AWS
managed policy ElasticLoadBalancingFullAccess is broader than most teams need, and the controller on EKS needs its own
policy (see Deploying Microservices on EKS).
For access logs, the S3 bucket policy must allow the ELB log-delivery principal for the Region, or in newer Regions the
logdelivery.elasticloadbalancing.amazonaws.com service principal; see the access logs guide. Prefer ACM-managed
certificates over uploaded ones, because ACM rotates them without redeployment. Restrict who can call
SetWebAcl and DisassociateWebACL, since disassociating the web ACL silently removes protection.
Scaling and availability
Elastic Load Balancing scales a load balancer automatically as traffic changes, by adding capacity and nodes. What you control is what is behind it:
-
Targets. Scale ECS services with Application Auto Scaling target tracking on
ALBRequestCountPerTarget, EC2 Auto Scaling groups the same way, and EKS with HPA, KEDA and Karpenter; see ECS Deployments, Scaling and Networking, Amazon EC2 and Auto Scaling and Deploying Microservices on EKS. -
Availability Zones. Enable at least two and keep targets in each; with cross-zone off (the NLB default), an Availability Zone with no healthy targets is removed from DNS.
-
Sudden spikes. Load balancer capacity grows gradually. For a planned burst (a sale, a marketing event) contact AWS Support about pre-warming, or use a Capacity Unit reservation (LCU reservation) on an ALB or NLB to hold minimum capacity.
-
Connection draining. The deregistration delay protects in-flight requests during scale-in and deployments.
-
Multi-Region. Combine Route 53 health checks (Route 53, DNS and ACM Certificates) or Global Accelerator with load balancers in each Region.
Pricing model
Prices change and differ by Region; use the Elastic Load Balancing pricing page and the AWS Pricing Calculator for current figures, which this page deliberately does not quote.
-
Hourly charge. Each load balancer is billed for every hour or partial hour it exists, whether or not it receives traffic. An idle test ALB or NLB is one of the most common surprise costs.
-
Capacity units. On top of the hourly charge an ALB is billed in Load Balancer Capacity Units (LCUs) and an NLB in Network LCUs (NLCUs); a GWLB uses GLCUs. An LCU measures the highest of four dimensions per hour: new connections per second, active connections per minute, processed bytes, and rule evaluations (rules processed times request rate). An NLCU uses new flows, active flows and processed bytes. You pay for whichever dimension dominates, so a chatty API with many rules is driven by rule evaluations, and a bulk-transfer service by bytes.
-
Public IPv4 addresses. Each public address on an internet-facing load balancer is billed per hour.
-
Data transfer. Traffic from the load balancer to targets in another Availability Zone, and out to the internet, follows standard EC2 data transfer rates; keep chatty paths in one zone where you can.
-
Classic Load Balancer. Billed per hour plus per gigabyte processed.
-
Related services. AWS WAF (per web ACL, rule and request), Global Accelerator (hourly per accelerator plus a data transfer premium), VPC Lattice (hourly per service, per gigabyte and per request), NAT gateway (hourly plus per gigabyte), AWS Certificate Manager public certificates (free) and access logs (S3 storage).
-
Cost levers. Share one ALB across many services with host and path rules instead of one per service; delete unused load balancers and target groups; keep traffic inside a zone; compress responses; use CloudFront to cut the bytes the ALB processes.
Limits and quotas
Quotas are per Region and per account, and many are adjustable through Service Quotas. Check the current values in
Quotas for your
Application Load Balancers and the matching Network and Gateway Load Balancer pages. Typical dimensions to watch:
load balancers per Region, listeners per load balancer, rules per ALB listener, target groups per Region, targets per
load balancer and per target group, and certificates per listener. Design limits include a 60-second default idle
timeout on an ALB (configurable), a 350-second default idle timeout for NLB TCP flows (configurable from 60 to 6,000
seconds with the tcp.idle_timeout.seconds listener attribute; fixed at 350 for TLS listeners), and request header size limits that
produce 400 or 431 errors. Always check quotas before a large migration; hitting the rules-per-listener limit after
adding path rules for a hundred services is a classic surprise.
Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
|
The target closed the connection, sent a malformed response, or the TLS handshake to an HTTPS target failed. |
Application logs; keep-alive timeout on the target must be longer than the ALB idle timeout (60 seconds by default); protocol of the target group matches the container. |
|
No registered healthy targets in the target group, or the target group is empty. |
|
|
The target did not answer within the idle timeout, or the target security group, route or NACL blocks the load balancer. |
Security group allowing the ALB group on the traffic port; slow queries; raise |
|
Malformed request, invalid headers (if dropped), or headers larger than the limit. |
Client request; |
|
An AWS WAF rule blocked the request, or an authentication rule denied it. |
WAF sampled requests and logs; web ACL in count mode first. |
|
|
ALB access logs |
Target |
The health check returned a status other than the matcher. |
Run the health check path from inside the VPC; widen the matcher only if the redirect is intended. |
Target |
The health check could not connect before the timeout. |
Security group allows the load balancer to reach the health check port; the container listens on |
ECS tasks restart in a loop after deployment |
The health check fails during slow start-up so the scheduler replaces the task. |
|
Users lose their session after a scale-out |
The application keeps state in memory and stickiness is off or expired. |
Move state to a shared store, or enable stickiness ( |
HTTPS shows a certificate warning |
The listener uses a different certificate, or the certificate does not cover the host name. |
|
Rules not matching |
Priorities, case sensitivity (paths are case-sensitive) or a missing wildcard. |
|
Kubernetes load balancer is never created |
The controller lacks IAM permissions, the subnets are not tagged, or the CRDs are missing. |
Controller logs with |
Useful commands and tools: aws elbv2 describe-target-health, aws elbv2 describe-load-balancer-attributes, the CloudWatch
metrics HTTPCode_ELB_5XX_Count, HTTPCode_Target_5XX_Count, TargetResponseTime, UnHealthyHostCount and
RejectedConnectionCount in the AWS/ApplicationELB namespace, ALB access logs queried with Amazon Athena, and VPC
Reachability Analyzer for path problems (see VPC Networking).
Clean up
Load balancers bill by the hour, so remove what this page created, in dependency order. Disable deletion protection first, disassociate the web ACL, and remove Global Accelerator and VPC Lattice resources before the load balancers they point at.
# Global Accelerator: disable, then delete in reverse order (control plane is in us-west-2)
aws globalaccelerator delete-endpoint-group --endpoint-group-arn "$EG_ARN" --region us-west-2
aws globalaccelerator delete-listener --listener-arn "$LIS_ARN" --region us-west-2
aws globalaccelerator update-accelerator --accelerator-arn "$ACC_ARN" --no-enabled --region us-west-2
aws globalaccelerator delete-accelerator --accelerator-arn "$ACC_ARN" --region us-west-2
# VPC Lattice
aws vpc-lattice delete-service-network-service-association --service-network-service-association-identifier "$SNSA_ID"
aws vpc-lattice delete-service-network-vpc-association --service-network-vpc-association-identifier "$SNVA_ID"
aws vpc-lattice delete-service --service-identifier "$SVC_ID"
aws vpc-lattice delete-target-group --target-group-identifier "$LTG_ID"
aws vpc-lattice delete-service-network --service-network-identifier "$SN_ID"
# AWS WAF
aws wafv2 disassociate-web-acl --resource-arn "$ALB_ARN"
aws wafv2 delete-web-acl --name bookshelf-web-acl --scope REGIONAL --id "$WEB_ACL_ID" --lock-token "$LOCK_TOKEN"
# Load balancers, listeners first, then target groups
aws elbv2 modify-load-balancer-attributes --load-balancer-arn "$ALB_ARN" \
--attributes Key=deletion_protection.enabled,Value=false
aws elbv2 delete-load-balancer --load-balancer-arn "$ALB_ARN"
aws elbv2 delete-load-balancer --load-balancer-arn "$NLB_ARN"
aws elbv2 delete-load-balancer --load-balancer-arn "$GWLB_ARN"
aws elbv2 wait load-balancers-deleted --load-balancer-arns "$ALB_ARN" "$NLB_ARN" "$GWLB_ARN"
aws elbv2 delete-target-group --target-group-arn "$TG_CATALOG"
aws elbv2 delete-target-group --target-group-arn "$TG_ORDERS"
aws elbv2 delete-target-group --target-group-arn "$TG_TCP"
aws elbv2 delete-target-group --target-group-arn "$TG_GENEVE"
# Security groups last (they cannot be deleted while a load balancer or task still uses them)
aws ec2 delete-security-group --group-id "$SG_TASKS"
aws ec2 delete-security-group --group-id "$SG_ALB"
Deleting a load balancer does not delete its targets (the ECS service or the EC2 instances), the ACM certificate, the
access-log bucket, or CloudWatch alarms; delete those with the pages that created them. Deleting a listener deletes its
rules; deleting a load balancer deletes its listeners. On Kubernetes delete the Gateway, Ingress or Service
object and let the controller remove the load balancer rather than deleting it in the console.
Related pages
-
VPC Networking covers subnets, security groups, NAT gateways, endpoints and PrivateLink.
-
Route 53, DNS and ACM Certificates covers DNS aliases to a load balancer and ACM certificates.
-
API Gateway is the managed API front door, and API Gateway and Backends for Frontends the architecture pattern.
-
CloudFront and CDN Delivery sits in front of an ALB for caching and edge protection.
-
ECS and Fargate and ECS Deployments, Scaling and Networking register tasks in the target groups described here.
-
Deploying Microservices on EKS shows the AWS Load Balancer Controller and the Gateway API in full.
-
Amazon EC2 and Auto Scaling shows an Auto Scaling group behind an ALB.
-
Gateways and Load Balancing is the Azure counterpart.