AWS Load Balancer Controller lets you manage Elastic Load Balancers in Kubernetes environments.
It supports resource types such as Ingress and LoadBalancer, and on AWS it can provision Application Load Balancers, Network Load Balancers, and Classic Load Balancers.
⚙️ How It Works
For Ingress
Reconciliation Flow

For a single Ingress resource, the controller behaves as follows:
- The controller watches Ingress events from the API server.
If the conditions are met, it starts creating AWS resources. - An ALB is created for the new Ingress resource.
It can be either internet-facing or internal. - AWS creates target groups based on the Ingress resource.
- Listeners are created for the configured ports. By default, ports 80 or 443 are used.
You can also attach certificates through annotations. - Rules are created from the Ingress definition.
Traffic is then routed to the appropriate Kubernetes Service.
The Load Balancer Controller also performs the following tasks:
- Removes related AWS resources when an Ingress is deleted in Kubernetes
- Updates AWS resources when an Ingress changes
- Reconstructs and reconciles related AWS resources after the controller restarts
Traffic Handling
The Load Balancer Controller supports two traffic modes:
- Instance mode (default): The ALB sends traffic to Kubernetes nodes, which then forward it through the Service’s NodePort.
- IP mode: The ALB sends traffic directly to Kubernetes pods. Your CNI must support direct access to pod IPs.
- This reduces hops and generally provides better performance.
- Configure it with the
alb.ingress.kubernetes.io/target-type: ipannotation.
For Gateway API

The controller follows a reconciliation loop like this:
- API Monitoring: The controller continuously monitors Gateway API resources through the API server.
- Queueing: Detected resources are added to an internal queue.
- Processing: For each queued item:
- It checks whether the referenced
GatewayClassis managed by its controller. In other words, it verifies thatspec.controllerNamein theGatewayClassis eithergateway.k8s.aws/alborgateway.k8s.aws/nlb. - Gateway API definitions are mapped to AWS resources such as NLB/ALB listeners, rules, target groups, and add-ons.
- The mapped resources are compared against the actual AWS state.
If the current state differs from the desired state, the controller calls the AWS API to synchronize it.
- It checks whether the referenced
- Status Updates: After reconciliation, the controller updates the
statusfield of theGatewayresource.
This provides real-time feedback such as the load balancer DNS name, ARN, and whether the Gateway has been accepted and programmed.
NOTE: If this is Gateway API, why does it also support NLB?
Gateway API is not limited to L7 routing. It can also replace L4 routing scenarios.
In other words, it can serve as an alternative totype: LoadBalancer.
A single Gateway cannot mix route types from different layers.
For example,HTTPRouteandTCPRoutecannot be used on the same Gateway.
- L7 Gateway:
HTTPRoute,GRPCRoute- L4 Gateway:
TCPRoute,UDPRoute,TLSRoute
Gateway API also supports both Instance mode and IP mode.
🚀 Installing AWS Load Balancer Controller
Installation with Helm
Add the Helm repository with the following command:
| |
| |
Or you can define the Helm values in values.yaml and install from that:
| |
| |
Other useful values
In addition to the Helm values above, you may also want to consider the following:
autoscaling: Adds an HPA for the controller. It can be useful if changes happen frequently.enableWafv2: Enables integration with AWS WAF v2.enableCertManager: Lets cert-manager manage the TLS certificates used by the admission webhook.
For more details, refer to this README.
📝 Hands-on Example
We will create an ALB-based Gateway API setup to provide L7 rule-based routing.
We will attach ACM for TLS termination, apply WAF ACL rules, and enable S3 access logging.
We will also create a policy that blocks external access to the /metrics path.
Required components
- An EKS cluster
- AWS Load Balancer Controller (see the installation section above)
- The controller must assume a role through IRSA or Pod Identity with the IAM policy required to access AWS APIs
- The installation manifest from the Gateway API CRD
Additional components
Route53 Hosted Zone, ACM Certificate, and External-DNS
These are listed as additional components, but in practice they are almost mandatory.
In real environments, you usually do not expose an ALB by itself. You attach a custom domain.
Below is an example Terraform configuration that creates a Route53 hosted zone and issues an ACM certificate.
If you purchased the domain in the AWS console, the hosted zone may already exist and should then be imported.
| |
You can install External-DNS from here.
Below is an example Helm values file:
| |
The required IAM policy is shown below.
Attach this policy through IRSA or Pod Identity.
| |
External-DNS works with the following logic:
- External-DNS extracts DNS name candidates from
HTTPRouteand similar resourcesspec.hostnameexternal-dns.alpha.kubernetes.io/hostname
- It identifies the parent Gateway from the
parentssection in theHTTPRoutestatus - It checks whether the Gateway accepted the route
- It matches Gateway listeners with the route information
- It checks
Gateway.status.addresses[].valueto find the load balancer address - It creates the DNS records
WAFv2 regional ACL
You can create a WAFv2 ACL with regional scope and attach it to the ALB.
In this example, you create only the ACL itself, not the association.
The Load Balancer Controller will create the association for the ALB.
S3 for access logging
ALB access logs can be written directly to S3, not to a CloudWatch log group.
The following options are generally recommended:
- Lifecycle policy
- Block Public Access
- KMS encryption when needed
For the ALB to write logs to S3, the following policy must be attached to the S3 bucket:
| |
GatewayClass
You must select gateway.k8s.aws/alb in spec.controllerName.
| |
LoadBalancerConfiguration
LoadBalancerConfiguration is a CRD available after installing AWS Load Balancer Controller, and it lets you configure additional AWS ELB settings.
The Gateway resource contains options that fit the Gateway API specification, while this CRD holds the AWS-specific configuration.
| |
If loadBalancerSubnets is left empty, the AWS subnets must have the following tags:
- For a public load balancer (
internet-facing):"kubernetes.io/role/elb" = "1" - For a private load balancer (
internal):"kubernetes.io/role/internal-elb" = "1"
Gateway
The Gateway must reference the name of the GatewayClass.
It can also reference the LoadBalancerConfiguration to use additional load balancer options.
Once the Gateway resource is created, the actual AWS ELB is provisioned and mapped to listeners.
| |
HTTPRoute
HTTPRoute is mapped to actual rules and target groups.
In this example there are two rules. One blocks external requests to /metrics.
This is explained in more detail below.
The second rule forwards traffic for the root path (/) to the web server.
If multiple prefixes match, PathPrefix prefers the more specific path.
| |
ListenerRuleConfiguration
The following rule is attached to /metrics and returns a fixed 403 response.
| |
TargetGroupConfiguration
The ELB needs health checks for the target group.
So you need to define a health check endpoint.
Also, just as Ingress uses annotations to choose between IP and Instance targets, here you define that with targetType.
| |
🌐 Result Example
Below is an example resource map created by the Load Balancer Controller after applying the configuration.

The reason there were no healthy pods in the target group in the screenshot is that I had removed all worker nodes at that time.
When healthy targets do exist, you can see the pod IPs inside each target group.
For convenience during the project, I had also exposed Grafana and ArgoCD publicly.
You can also see that Route53 records were created.

The S3 access logs are also being stored correctly.

