# Service Type LoadBalancer

> Every Service of type LoadBalancer provisions a separate cloud load balancer, each billing hourly plus its own public IP. Ten such Services means 10 load balancers. An Ingress controller fronts many services behind 1, which is usually the cheaper architecture.

Source: https://zop.dev/integrations/kubernetes/recommendations/service-type-loadbalancer
Updated: 2026-08-19

---

## One Service, one load balancer, one bill

`type: LoadBalancer` asks the cloud provider for a dedicated load balancer. Not a route on a
shared one, but a distinct resource with its own hourly charge and its own public IPv4 address,
which is separately billable on every major cloud.

The arithmetic is linear and unforgiving: ten services exposed this way produce ten load
balancers and ten addresses, none of which are visible from inside the cluster.

## Why Ingress is usually the answer

An Ingress controller provisions **one** load balancer and routes to many backend services by
host and path. Adding the eleventh service adds an Ingress rule, not another cloud resource.

The exceptions are real, and worth naming so this finding is not applied blindly:

- **Non-HTTP traffic.** Ingress is HTTP/HTTPS. A gRPC stream, a database protocol or raw TCP
  usually needs a LoadBalancer Service or a Gateway API implementation.
- **Deliberate isolation.** A tenant or environment that must not share an ingress path.
- **Protocol features** the ingress controller does not support: certain proxy protocols, some
  health-check semantics.

## The security dimension

Each LoadBalancer Service is a public entry point. Ten of them is ten attack surfaces to
configure, monitor and keep patched, versus one ingress with a single TLS configuration and a
single place to apply rate limiting or WAF rules.

That consolidation is often worth more than the cost saving.

## LoadBalancer Services and their ingress IPs

```bash
kubectl get svc -A -o json | jq -r '
  .items[] | select(.spec.type=="LoadBalancer")
  | "\(.metadata.namespace)/\(.metadata.name)\t\(.status.loadBalancer.ingress[0].ip // "pending")"'
```

Count the rows, then compare against your cloud console's load balancer list. They should match,
and a mismatch means orphaned balancers from deleted Services.

## Before consolidating

Check whether anything external allowlisted the individual IPs. Moving behind an ingress changes
the address clients connect to, and a partner firewall rule pointing at the old load balancer
will break at cutover rather than at deploy.
