Skip to main content

Configure a Hetzner load balancer

Inspect 1.36

A Syself Autopilot cluster uses two kinds of Hetzner load balancers, and you configure them in two different places. The control-plane load balancer carries API server traffic and is set through topology variables on the Cluster object. Application load balancers carry your workload traffic and are set through annotations on each type: LoadBalancer Service. If you are still deciding whether a load balancer is the right way to expose an application, start with .

Kind Who creates it How you configure it
Control-plane load balancer Syself Autopilot, at cluster creation Topology variables on the Cluster object
Application load balancer The Hetzner CCM, when you create a LoadBalancer Service Annotations on the Service
flowchart LR
  A["kubectl and API clients"] --> CPLB["Control-plane LB :443"] --> API["Control-plane nodes :6443"]
  U["Internet clients"] --> APPLB["Per-Service LB"] --> NP["Node NodePort"] --> POD["Pods"]
Note

Hetzner load balancers are TCP only (they also do HTTP and HTTPS, which run on TCP). There is no UDP load balancer. To expose a UDP service, reach the nodes with a NodePort instead; see .

The control-plane load balancer#

Syself Autopilot creates one Hetzner load balancer for the Kubernetes API server when it builds the cluster. It listens on port 443, forwards to port 6443 on the control-plane nodes, and carries only API traffic. The Hetzner CCM does not manage this one. The platform creates and reconciles it from your topology variables, so you configure it on the Cluster object, not with Service annotations.

cluster.yamlyaml
		spec:
  topology:
    variables:
      - name: clusterLoadBalancerType
        value: lb21 # lb11 (default) | lb21 | lb31
      - name: clusterLoadBalancerName
        value: my-apiserver-lb # optional; Syself names it if you omit this
      - name: region
        value: nbg1 # fsn1 (default) | nbg1 | hel1 | ash | hil
	

clusterLoadBalancerType sets the Hetzner product. A load balancer type is defined by two limits: how many targets (the servers behind it) it can hold, and how many simultaneous connections it can carry.

Type Max targets Max connections
lb11 25 10,000
lb21 75 20,000
lb31 150 40,000

Choose the smallest type that covers your control-plane node count and its peak connections; lb11 is sufficient for most clusters, since a control plane has few nodes. The same types and limits apply to the per-Service load balancers below through the type annotation. See the Hetzner load balancer page for the current numbers.

Changing the type on a running cluster resizes the load balancer in place, so it keeps its IP addresses. The region variable sets both the control plane's placement and the load balancer's region at once; the load balancer cannot live in a different region from the cluster.

Read the current settings from the HetznerCluster object:

		$ kubectl get hetznercluster <cluster-name> -o jsonpath='{.spec.controlPlaneLoadBalancer}'
	

The output shows enabled, algorithm, type, port, region, targetAddressFamily, enableProxyProtocol, and the healthCheck settings.

Application load balancers#

The CCM watches for Services with type: LoadBalancer. For each one it creates a Hetzner load balancer, connects your pods as targets through a NodePort, and writes the public address into status.loadBalancer.ingress.

Note

The Hetzner load balancer is not the only way to serve a type: LoadBalancer Service: you can self-host the load-balancing layer with a project like MetalLB, which announces Service IPs from the nodes themselves. Like any add-on, it is yours to run and operate. The rest of this page covers the managed Hetzner load balancers.

service.yamlyaml
		apiVersion: v1
kind: Service
metadata:
  name: my-app
  annotations:
    load-balancer.hetzner.cloud/location: fsn1
    load-balancer.hetzner.cloud/name: my-app-lb
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
	

The CCM reconciles a Service whenever it changes, so most annotation edits take effect within seconds.

Defaults you get with no annotations#

The platform configures the CCM so a bare LoadBalancer Service already works:

  • Dual-stack address. Every load balancer receives a public IPv4 and a public IPv6 address, and bridges IPv6 clients to the IPv4-only nodes. Disable IPv6 per Service with load-balancer.hetzner.cloud/ipv6-disabled: "true". See .
  • Public-IP targeting. The load balancer reaches nodes over their public IP, because the platform runs no private network. You never set use-private-ip.
  • Default region. A Service with no location receives a load balancer in the cluster's region. Set location regardless, so the region is explicit and does not drift from where your workload runs.

Annotation reference#

Every annotation goes on metadata.annotations of the Service.

Annotation (load-balancer.hetzner.cloud/…) What it does Notes
location Region for the load balancer (fsn1, nbg1, hel1, ash, hil) Defaults to the control-plane load balancer's region. Set it explicitly.
type Product: lb11, lb21, or lb31 lb11 is the default.
name Name shown in the Hetzner console Optional.
algorithm-type Target selection: round_robin or least_connections round_robin is the default.
ipv6-disabled "true" removes the public IPv6 IPv6 is on by default.
hostname Publishes a hostname in status.loadBalancer.ingress instead of the IPs With this set, the IP and ipMode reads on the Service return nothing.
uses-proxyprotocol "true" enables PROXY protocol The backend must also parse the header. See .
health-check-protocol tcp, http, or https tcp is the default.
health-check-interval Interval between checks, a Go duration such as "15s" A bare number is rejected and the Service fails to reconcile.
health-check-timeout Timeout of a single check, a Go duration such as "10s"
health-check-retries Failed checks before a target is marked unhealthy
health-check-http-path Path requested by http and https checks
robot-target-address-family Address family for bare-metal (Robot) targets Platform default ipv4. See .

Where to go next#

For where load balancers sit in the wider networking picture, see .