Skip to content
Beta

Configure a LoadBalancer Service

A Service of type LoadBalancer gets a platform load balancer on your workspace's public address (see Kubernetes, and Load balancers for where clients connect). You configure that load balancer from the Service itself, with the standard loadBalancerSourceRanges field and a few annotations under lb.sahabti.com/. The cluster is the source of truth: the console shows these settings on the load balancer's page, read-only, each with the field or annotation it comes from.

On the load balancer's Overview, the console shows where clients connect as an endpoint for your apps: its hostname with the public port, such as prod-default-db-e5f6a7b8-q2w7ze.lb.sahabti.app:40002, and the protocol, ready to copy. It is not a website address: websites use the cluster URL, https://<cluster>.sahabti.app, served by the cluster's managed gateway.

What you can set

SettingOn the ServiceValuesDefault
Allowed sourcesspec.loadBalancerSourceRangesUp to 20 IPv4 addresses or rangesAnyone
Denied sourceslb.sahabti.com/denied-sourcesUp to 200 IPv4 addresses or ranges, separated by commasNone
Protection profilelb.sahabti.com/protection-profilestandard or strictstandard
Idle timeoutlb.sahabti.com/idle-timeoutWhole seconds, from 10 to 360060
Client IPlb.sahabti.com/client-ipoff or proxy-protocoloff
Short-connection banslb.sahabti.com/short-session-banson or offon

Each setting applies to every port of the Service and takes effect within seconds. Remove a field or an annotation and its setting goes back to the default.

Allow only some sources

yaml
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 443
      targetPort: 8443
  loadBalancerSourceRanges:
    - 203.0.113.0/24
    - 198.51.100.7/32

Only these sources can connect to the Service's ports; everyone else is dropped. To change the list on a running Service:

bash
kubectl patch service web -p '{"spec":{"loadBalancerSourceRanges":["203.0.113.0/24"]}}'
  • A bare address counts as a /32, and 0.0.0.0/0 anywhere in the list means anyone.
  • The load balancer has an IPv4 address only, so IPv6 ranges are ignored. A list of IPv6 ranges only cannot be applied.
  • A range that another listed range already holds is dropped.

Deny sources

yaml
metadata:
  annotations:
    lb.sahabti.com/denied-sources: "192.0.2.15, 198.51.100.0/24"
bash
kubectl annotate service web lb.sahabti.com/denied-sources="192.0.2.15,198.51.100.0/24" --overwrite

Denied sources are dropped before they reach any port of the load balancer, for as long as they are listed. Separate them with commas, spaces or new lines. Sources the load balancer bans on its own for tripping a protection limit are separate: they expire by themselves and show under Blocked sources on the load balancer's Security page.

Pick a protection profile

Every load balancer limits new and concurrent connections and floods per source, and bans a source that trips a limit. strict tightens the limits, which suits exposed databases and admin ports:

standardstrict
New connections per second, per source5020
Concurrent connections, per source20050
Connections from a source that end within 4 seconds, per minute10030
Ban for a source that trips a limit10 minutes1 hour
SYN packets per second, per port5,0002,000
UDP packets per second, per port20,0005,000
bash
kubectl annotate service web lb.sahabti.com/protection-profile=strict --overwrite

For ports that legitimately see many short connections, such as monitoring probes or clients behind one office address, set lb.sahabti.com/short-session-bans: "off" to stop banning sources for them; every other limit still applies.

Set the idle timeout

How long a TCP connection may stay idle, with nothing sent either way, before the load balancer closes it. Raise it for long-lived connections such as WebSockets, message queues or database sessions that sit quiet between queries.

bash
kubectl annotate service web lb.sahabti.com/idle-timeout=900 --overwrite

Pass the client's address

Your pods see connections coming from the load balancer, not from the client. With proxy-protocol, every TCP connection begins with a PROXY protocol v2 header that carries the client's address and port:

bash
kubectl annotate service web lb.sahabti.com/client-ip=proxy-protocol --overwrite

Turn PROXY protocol on in the server that receives the connections, such as your ingress controller or web server, before you set the annotation: a server that does not expect the header treats it as a broken request. UDP ports never carry the header.

preserve, which keeps the client's address on the packets themselves, is not available for a cluster's load balancer: the cluster's nodes do not route their replies through it.

A complete example

yaml
apiVersion: v1
kind: Service
metadata:
  name: shop
  annotations:
    lb.sahabti.com/protection-profile: strict
    lb.sahabti.com/idle-timeout: "300"
    lb.sahabti.com/denied-sources: "192.0.2.15"
    lb.sahabti.com/client-ip: proxy-protocol
spec:
  type: LoadBalancer
  selector:
    app: shop
  ports:
    - name: https
      port: 443
      targetPort: 8443
  loadBalancerSourceRanges:
    - 203.0.113.0/24

Quote numbers in annotations, as above: annotation values are strings.

Check what applied

The Service's events and its lb.sahabti.com/SettingsApplied condition say what the platform made of its settings:

bash
kubectl describe service shop
kubectl get service shop -o jsonpath='{.status.conditions}'
StatusReasonWhat it means
TrueAppliedThe load balancer follows every setting of the Service.
FalseInvalidSettingsAt least one value cannot be applied. The message names each one; those settings keep their last valid value and every other setting is applied.
FalseNotSupportedThe platform does not apply settings from the cluster yet. They take effect once it does, with no change on your side.

A Warning event, LoadBalancerSettingsInvalid, is recorded when a value cannot be applied, and a Normal event, LoadBalancerSettingsApplied, once every setting applies again. A Service that sets none of these has no such condition.

On the load balancer's page in the console, Overview lists each setting with the field or annotation it comes from, the default when the Service does not set it, and any value that could not be applied.

When a value cannot be applied

An invalid value never stops the Service and never opens it wider: that setting keeps its last valid value, the other settings still apply, and the condition says why. A new load balancer is the one exception. If its allowed or denied sources cannot be applied when it is created, it stays closed, with no port answering and no external IP on the Service, until they are fixed.

CauseFix
loadBalancerSourceRanges lists only IPv6 rangesList IPv4 ranges: the load balancer has an IPv4 address only.
More than 20 allowed or 200 denied sourcesList fewer, or wider, ranges.
idle-timeout with a unit, such as 300s, or outside 10 to 3600Write whole seconds: "300".
protection-profile other than standard or strictUse one of the two, in lowercase.
client-ip: preserveUse proxy-protocol.
short-session-bans other than on or offUse one of the two, in lowercase.
A typo in an annotation name, such as lb.sahabti.com/idle-timoutThe condition names the annotation it does not know; fix its name.

Settings changes are limited to 30 a minute for each cluster. Past that, the platform applies the latest settings a few seconds later.