Amazon EKS — AWS Load Balancer Controller
The AWS Load Balancer Controller creates and manages ALBs and NLBs for Kubernetes Ingress and Service resources. It replaces the legacy in-tree AWS cloud provider load balancer.
What It Replaces
| Feature | Legacy (in-tree) | AWS LB Controller |
|---|---|---|
| ALB support | No | Yes (via Ingress) |
| NLB support | Basic | Full (IP mode, cross-zone) |
| Target type | Instance only | Instance or IP |
| WAF integration | No | Yes |
| IngressGroup | No | Yes (shared ALB) |
| Active development | Deprecated | Active |
Installation with Helm
Step 1 — Create IAM Role
Download the IAM policy:
curl -o iam-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.9.0/docs/install/iam_policy.json
Create the IAM policy:
aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam-policy.json
Create the IAM role with IRSA:
eksctl create iamserviceaccount \
--cluster my-cluster \
--namespace kube-system \
--name aws-load-balancer-controller \
--attach-policy-arn arn:aws:iam::123456789:policy/AWSLoadBalancerControllerIAMPolicy \
--approve
Step 2 — Install with Helm
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
--namespace kube-system \
--set clusterName=my-cluster \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller \
--set region=us-east-1 \
--set vpcId=vpc-0123456789abcdef0
Verify
kubectl get deployment aws-load-balancer-controller -n kube-system
kubectl logs -n kube-system deployment/aws-load-balancer-controller
Subnet Discovery Tags
The controller discovers subnets using tags:
| Tag | Value | Purpose |
|---|---|---|
kubernetes.io/role/elb |
1 |
Public subnets — internet-facing ALB/NLB |
kubernetes.io/role/internal-elb |
1 |
Private subnets — internal ALB/NLB |
EKS also requires:
kubernetes.io/cluster/my-cluster = owned (or shared)
Controller Architecture
The controller watches Kubernetes Ingress and Service resources, then:
- Ingress → Creates/updates an ALB with target groups and rules
- Service (type: LoadBalancer) → Creates/updates an NLB with target groups
- TargetGroupBinding → Binds existing target groups to K8s endpoints
The controller reconciles the AWS resources continuously — if someone manually changes the ALB in the console, the controller reverts it.
Terraform Installation
module "lb_controller_irsa" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "~> 5.0"
role_name = "aws-load-balancer-controller"
attach_load_balancer_controller_policy = true
oidc_providers = {
main = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = ["kube-system:aws-load-balancer-controller"]
}
}
}
resource "helm_release" "aws_lb_controller" {
name = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
chart = "aws-load-balancer-controller"
namespace = "kube-system"
version = "1.9.0"
set {
name = "clusterName"
value = module.eks.cluster_name
}
set {
name = "serviceAccount.create"
value = "true"
}
set {
name = "serviceAccount.name"
value = "aws-load-balancer-controller"
}
set {
name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = module.lb_controller_irsa.iam_role_arn
}
}
Best Practices
- Tag subnets correctly — the controller cannot discover subnets without proper tags
- Use IP target mode — better latency (skips kube-proxy), required for Fargate
- Run two replicas of the controller for high availability
- Pin the controller version — test upgrades before applying
- Monitor controller logs — most issues are visible in controller pod logs