Kubernetes
AWS
LoadBalancer
Static IP
Cloud Computing

assign static IP to LoadBalancer service using k8s on aws

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

On AWS, a Kubernetes Service of type LoadBalancer does not automatically mean "one fixed public IP forever." If you need a stable public address for firewall allowlists or partner integrations, the practical answer is usually to use a Network Load Balancer, or NLB, with Elastic IP allocations rather than relying on the default behavior of older AWS load balancers.

Why a Plain LoadBalancer Service Is Not Enough

Historically, Kubernetes on AWS often created a Classic Load Balancer or an NLB depending on controller choice and annotations. A DNS name was always stable enough for most applications, but the underlying IP addresses were not something you should treat as manually assigned unless the load balancer type explicitly supported that model.

That distinction matters because "static IP" on AWS usually means one of these:

  • a stable DNS name
  • an Elastic IP, or EIP, attached to a public endpoint
  • a pair of fixed Anycast addresses through another AWS service

For a Kubernetes Service, the most direct fit is an internet-facing NLB with EIPs.

If you are using the AWS Load Balancer Controller, you can annotate a LoadBalancer service so the controller provisions an NLB and associates preallocated EIPs. The important operational detail is that you normally need one EIP allocation per subnet used by the NLB.

yaml
1apiVersion: v1
2kind: Service
3metadata:
4  name: api-service
5  annotations:
6    service.beta.kubernetes.io/aws-load-balancer-type: "external"
7    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
8    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
9    service.beta.kubernetes.io/aws-load-balancer-eip-allocations: "eipalloc-0123456789abcdef0,eipalloc-0fedcba9876543210"
10spec:
11  type: LoadBalancer
12  selector:
13    app: api
14  ports:
15    - name: http
16      port: 80
17      targetPort: 8080

The flow is usually:

  1. Allocate the Elastic IPs in AWS first.
  2. Make sure your cluster subnets are tagged correctly for the controller.
  3. Create the service with NLB annotations.
  4. Verify that the created load balancer is an NLB and that the EIPs are attached.

Using ip target mode is common because traffic goes directly to pod IPs, but instance mode is also possible depending on your networking model.

When This Does Not Fit

There are two cases where people often need a different design.

The first is when they are using a controller mode that does not support the annotation set they expect. In that case, you can provision the NLB outside Kubernetes and connect it to the cluster through AWS Load Balancer Controller resources such as TargetGroupBinding.

The second is when they really want a stable entry point in front of an Application Load Balancer. In that scenario, the answer is not usually "attach an EIP to the ALB," because that is not how ALBs are exposed. You would instead use a different AWS architecture in front of it, or accept DNS as the contract.

A Good Verification Checklist

After applying the service manifest, verify the result instead of assuming the annotations were honored:

bash
kubectl get svc api-service
kubectl describe svc api-service

Then check the AWS side:

  • the load balancer type is Network Load Balancer
  • the scheme is internet-facing if you want public access
  • each subnet has the expected Elastic IP allocation
  • health checks are passing

If you see a DNS name but the AWS resource is not the kind of NLB you expected, the wrong controller may be reconciling the service.

Operational Tradeoffs

A fixed public IP is useful, but it increases coupling to infrastructure details. Whenever possible, it is still better to publish a DNS name and treat the IP as an implementation detail. Static IPs make sense when an external system cannot consume DNS reliably, or when contractual allowlisting requires literal addresses.

Also remember that an NLB can expose multiple static addresses, one per Availability Zone. That is normal behavior and often desirable for resilience. Consumers may need to allowlist all of them rather than assuming a single address.

Common Pitfalls

The biggest mistake is assuming every AWS load balancer behind a Kubernetes service supports manually pinned public IPs. That is not true, and it leads to confusing half-working setups.

Another common issue is allocating fewer EIPs than subnets. The controller expects the counts to line up, so mismatches can prevent the service from reconciling correctly.

People also forget that annotation support depends on which controller owns the service. The legacy service controller and the AWS Load Balancer Controller do not behave the same way, and EKS Auto Mode has its own rules as well.

Finally, do not treat the EXTERNAL-IP field from kubectl get svc as proof that the architecture is correct. Always inspect the actual AWS load balancer and confirm the EIP attachments.

Summary

  • On AWS, the usual way to get static public addresses for a Kubernetes service is an NLB with Elastic IPs.
  • Use AWS Load Balancer Controller annotations rather than assuming default LoadBalancer behavior is enough.
  • Preallocate one EIP per subnet used by the NLB.
  • Verify which controller created the load balancer and what resource type actually exists.
  • Prefer DNS when you can, and use fixed IPs only when the integration truly requires them.

Course illustration
Course illustration

All Rights Reserved.