LoadBalancer Configuration
LoadBalancer services expose applications to external traffic. KSail supports LoadBalancer across all distributions with distribution-specific implementations for each platform.
Overview
Section titled âOverviewâLoadBalancer support varies by Kubernetes distribution and infrastructure provider:
| Distribution | Provider | Implementation | Default Behavior | Configuration Required |
|---|---|---|---|---|
| Vanilla (Kind) | Docker | Cloud Provider KIND | Disabled | Yes |
| K3s (K3d) | Docker | ServiceLB (Klipper) | Enabled | No |
| Talos | Docker | MetalLB | Disabled | Yes |
| Talos | Hetzner | hcloud-cloud-controller-manager | Enabled | No |
| VCluster (Vind) | Docker | Delegated to host cluster | N/A | N/A |
VCluster delegates LoadBalancer to the host cluster â spec.cluster.loadBalancer has no effect and never triggers a cluster update. Use the --load-balancer flag or spec.cluster.loadBalancer in ksail.yaml with values Default, Enabled, or Disabled.
Platform-Specific Configuration
Section titled âPlatform-Specific ConfigurationâVanilla (Kind) on Docker
Section titled âVanilla (Kind) on DockerâVanilla clusters use the Cloud Provider KIND controller, which runs as an external Docker container and allocates LoadBalancer IPs from the Docker bridge network.
Enable LoadBalancer Support
Section titled âEnable LoadBalancer SupportâCLI:
ksail cluster init \ --name my-cluster \ --distribution Vanilla \ --load-balancer Enabledksail.yaml:
spec: cluster: distribution: Vanilla loadBalancer: EnabledHow It Works
Section titled âHow It WorksâCloud Provider KIND runs as a single Docker container named ksail-cloud-provider-kind on the kind network, mounting the Docker socket to watch for type: LoadBalancer services. For each service it creates a dedicated cpk-<namespace>-<name> container that routes traffic from an IP on the kind network bridge subnet (typically 172.18.0.0/16) to the serviceâs pods. ksail cluster delete removes the controller and all cpk-* containers.
K3s on Docker
Section titled âK3s on DockerâK3s includes ServiceLB by default, assigning the cluster nodeâs IP as the external IP and forwarding traffic via iptables. No configuration needed:
ksail cluster init --name my-cluster --distribution K3sksail cluster createTo disable: use --load-balancer Disabled (CLI) or set loadBalancer: Disabled in ksail.yaml.
Talos on Docker
Section titled âTalos on DockerâTalos on Docker uses MetalLB to provide LoadBalancer services. MetalLB operates in Layer 2 mode and allocates IPs from a pre-configured pool.
Enable LoadBalancer Support
Section titled âEnable LoadBalancer SupportâCLI:
ksail cluster init \ --name my-cluster \ --distribution Talos \ --load-balancer Enabledksail.yaml:
spec: cluster: distribution: Talos provider: Docker loadBalancer: EnabledKSail configures MetalLB with a default IP pool of 172.18.255.200â172.18.255.250 in Layer 2 (ARP/NDP) mode on the Docker bridge network, chosen to avoid conflicts with typical Docker allocations.
How It Works
Section titled âHow It WorksâKSail installs MetalLB via Helm, configures an IPAddressPool and L2Advertisement automatically, and MetalLB assigns IPs from the pool via ARP on the Docker network.
Custom IP Pool
Section titled âCustom IP PoolâTo use a custom IP range, youâll need to create custom MetalLB resources after cluster creation:
apiVersion: metallb.io/v1beta1kind: IPAddressPoolmetadata: name: custom-pool namespace: metallb-systemspec: addresses: - 172.18.100.1-172.18.100.254---apiVersion: metallb.io/v1beta1kind: L2Advertisementmetadata: name: custom-l2 namespace: metallb-systemspec: ipAddressPools: - custom-poolTalos on Hetzner
Section titled âTalos on HetznerâTalos on Hetzner Cloud uses the Hetzner Cloud Controller Manager to provision real cloud load balancers. LoadBalancer is enabled by default â KSail automatically installs hcloud-ccm when loadBalancer is Default or Enabled.
Prerequisites: Set HCLOUD_TOKEN to a Hetzner API token with read/write permissions for Load Balancers.
export HCLOUD_TOKEN=your-token-hereksail cluster init \ --name my-cluster \ --distribution Talos \ --provider Hetznerksail cluster createThe Hetzner CCM provisions a real cloud load balancer with a public IP in 30â60 seconds (subject to Hetzner billing).
kubectl get svc my-app -w # EXTERNAL-IP appears after 30â60 secondsAnnotations
Section titled âAnnotationsâYou can customize Hetzner Load Balancer behavior using annotations:
apiVersion: v1kind: Servicemetadata: name: my-app annotations: load-balancer.hetzner.cloud/location: nbg1 load-balancer.hetzner.cloud/use-private-ip: "true" load-balancer.hetzner.cloud/health-check-interval: "15s"spec: type: LoadBalancer selector: app: my-app ports: - port: 80 targetPort: 8080See the Hetzner CCM documentation for all available annotations.
Troubleshooting
Section titled âTroubleshootingâLoadBalancer Service Stuck in Pending
Section titled âLoadBalancer Service Stuck in PendingâSymptom: EXTERNAL-IP shows <pending>.
Diagnosis:
# Check loadBalancer setting (absent field = Default; Default auto-enables on K3s and TalosĂHetzner):grep 'loadBalancer:' ksail.yaml || echo "(not set â effective value: Default)"# Vanilla: docker logs ksail-cloud-provider-kind# Talos: kubectl logs -n metallb-system -l app.kubernetes.io/component=controller# Hetzner: kubectl logs -n kube-system -l app=hcloud-cloud-controller-managerCommon fixes: LoadBalancer disabled â re-init with --load-balancer Enabled. Cloud Provider KIND not running â delete and recreate cluster. MetalLB IP pool exhausted â expand the pool (see below). Hetzner â ensure HCLOUD_TOKEN is set during cluster creation.
Cannot Access LoadBalancer IP
Section titled âCannot Access LoadBalancer IPâSymptom: Service has an external IP but connections fail.
Diagnosis:
kubectl get pods -l app=my-app # verify pods are Runningkubectl get endpoints my-app # check endpoint IPs are populatedkubectl logs -l app=my-app # review pod logs# Test from within the cluster:kubectl run test --rm -it --restart=Never --image=curlimages/curl -- curl http://my-app.default.svc.cluster.localCommon fixes: Wrong targetPort â match the container port. Network policy blocking traffic â inspect NetworkPolicy resources. Application not listening on 0.0.0.0 â fix app bind address.
MetalLB IP Pool Exhausted
Section titled âMetalLB IP Pool ExhaustedâSymptom: New LoadBalancer services remain <pending> after many allocations (default pool has 51 IPs: 172.18.255.200â172.18.255.250).
Fix: Create an additional IPAddressPool in metallb-system:
apiVersion: metallb.io/v1beta1kind: IPAddressPoolmetadata: name: expanded-pool namespace: metallb-systemspec: addresses: - 172.18.255.200-172.18.255.254 # Expanded from .250 to .254