LoadBalancer Services
4 minute read
In PSKE, Kubernetes Services of type LoadBalancer are provisioned through the OpenStack Cloud Controller Manager (CCM). The CCM automatically creates an Octavia LoadBalancer in OpenStack and assigns a floating IP to it.
Configuration is done exclusively via annotations on the Service object.
lb-provider=ovn.Annotation Reference
IP and Network Configuration
| Annotation | Default | Description |
|---|---|---|
service.beta.kubernetes.io/openstack-internal-load-balancer | false | Creates an internal LoadBalancer without a floating IP. Access is restricted to the internal network. |
loadbalancer.openstack.org/keep-floatingip | false | Retains the floating IP in OpenStack even after the Service is deleted. |
loadbalancer.openstack.org/hostname | — | Sets an explicit hostname in the Service status instead of the floating IP. |
loadbalancer.openstack.org/load-balancer-address | — | Automatically set after creation. Contains the floating IP. When hostname is set, this is the only place to find the actual IP. |
Proxy Protocol and Headers
| Annotation | Default | Description |
|---|---|---|
loadbalancer.openstack.org/proxy-protocol ¹ | false | Enables PROXY protocol. Values: v1 / true (version 1), v2 (version 2). Passes the original client IP to the backend. |
loadbalancer.openstack.org/x-forwarded-for ¹ | — | Adds the X-Forwarded-For header. Forces listener type HTTP. |
Load Balancing Behavior
| Annotation | Default | Description |
|---|---|---|
loadbalancer.openstack.org/lb-method | CCM config | LB algorithm. Values: ROUND_ROBIN, LEAST_CONNECTIONS, SOURCE_IP, SOURCE_IP_PORT. |
loadbalancer.openstack.org/connection-limit | -1 (unlimited) | Maximum connections per second per listener. Supports live updates. |
loadbalancer.openstack.org/load-balancer-id | — | Binds the Service to an existing Octavia LoadBalancer. Must not be changed after creation. |
Timeouts
| Annotation | Default | Description |
|---|---|---|
loadbalancer.openstack.org/timeout-client-data ¹ | 30000 ms | Frontend client inactivity timeout in milliseconds. |
loadbalancer.openstack.org/timeout-member-connect ¹ | — | Backend member connection timeout in milliseconds. |
loadbalancer.openstack.org/timeout-member-data ¹ | 30000 ms | Backend member inactivity timeout in milliseconds. |
loadbalancer.openstack.org/timeout-tcp-inspect ¹ | — | Time to wait for additional TCP packets for content inspection, in milliseconds. |
Use Cases
Internal LoadBalancer
An internal LoadBalancer does not receive a floating IP and is only reachable within the cluster network. The internal IP address can be specified via spec.loadBalancerIP (any free address from the node network, default: 10.250.0.0/16).
apiVersion: v1
kind: Service
metadata:
name: my-internal-service
annotations:
service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
spec:
type: LoadBalancer
loadBalancerIP: 10.250.0.10
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
Reserving and Reusing a Floating IP
By default, the floating IP is released when the Service is deleted. Setting keep-floatingip: "true" retains it in OpenStack.
Reserve a floating IP:
metadata:
annotations:
loadbalancer.openstack.org/keep-floatingip: "true"
Assign an already reserved floating IP:
metadata:
annotations:
loadbalancer.openstack.org/keep-floatingip: "true"
loadbalancer.openstack.org/load-balancer-address: <FloatingIP>
spec:
loadBalancerIP: <FloatingIP>
Release a floating IP (before deleting the Service):
metadata:
annotations:
loadbalancer.openstack.org/keep-floatingip: "false"
loadbalancer.openstack.org/load-balancer-address: <FloatingIP>
spec:
loadBalancerIP: <FloatingIP>
If it is no longer known which floating IPs are reserved, please open a support ticket in the customer portal.
Adjusting Timeouts
The default timeout for OpenStack LoadBalancers is 30 seconds. For applications with long-lived connections (e.g. WebSockets, large file uploads), the timeouts should be adjusted:
metadata:
annotations:
loadbalancer.openstack.org/timeout-client-data: "70000"
loadbalancer.openstack.org/timeout-member-data: "70000"
Proxy Protocol — Forwarding the Client IP
Without the Proxy Protocol, all incoming connections appear in the backend with the LoadBalancer’s internal IP as the source. The Proxy Protocol passes the original client IP through.
Annotation on the LoadBalancer Service:
metadata:
annotations:
loadbalancer.openstack.org/proxy-protocol: "true"
Ingress Controller — NGINX (ConfigMap):
use-proxy-protocol: "true"
use-forwarded-headers: "true"
Ingress Controller — Traefik (Helm values):
ports:
web:
proxyProtocol:
trustedIPs:
- "0.0.0.0/0"
websecure:
proxyProtocol:
trustedIPs:
- "0.0.0.0/0"
Gateway API — Traefik v3 (Helm values):
Traefik natively supports the Gateway API from v3 onwards. Proxy Protocol is configured at the EntryPoint level — regardless of whether Ingress or Gateway API resources are used:
ports:
web:
proxyProtocol:
trustedIPs:
- "0.0.0.0/0"
websecure:
proxyProtocol:
trustedIPs:
- "0.0.0.0/0"
Gateway API — Envoy Gateway:
Proxy Protocol is enabled via a ClientTrafficPolicy targeting the respective Gateway:
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: proxy-protocol
namespace: <gateway-namespace>
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: <gateway-name>
enableProxyProtocol: true
Proxy Protocol and Internal Services
When the Proxy Protocol is enabled, kube-proxy routes cluster-internal access to the external LoadBalancer IP directly to the Ingress controller — without speaking the Proxy Protocol. This causes connection errors, for example when Cert-Manager attempts an ACME challenge.
Solution: The annotation loadbalancer.openstack.org/hostname sets a DNS name in the Service status. kube-proxy uses this name instead of the IP, preserving the path through the LoadBalancer.
metadata:
annotations:
loadbalancer.openstack.org/proxy-protocol: "true"
loadbalancer.openstack.org/hostname: <loadbalancer-ip>.nip.io
For <loadbalancer-ip>.nip.io — or a custom DNS A record — the IP must resolve to the floating IP of the LoadBalancer.
Full upstream documentation is available in the GitHub project kubernetes/cloud-provider-openstack — use the release branch matching the deployed Kubernetes version.