Skip to main content

Overview

MetalLB is a load balancer implementation for bare metal Kubernetes clusters. It provides LoadBalancer-type service support, which is typically only available in cloud environments.

HelmRelease Configuration

MetalLB is deployed using Flux with the following HelmRelease:
helm-release.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: metallb
spec:
  chart:
    spec:
      chart: metallb
      sourceRef:
        kind: HelmRepository
        name: metallb
      version: "=0.15.3"
  interval: 24h
  releaseName: metallb
  install:
    crds: Create
  upgrade:
    crds: CreateReplace

Key Configuration

spec.chart.spec.version
string
required
MetalLB Helm chart version. Currently using 0.15.3
spec.interval
string
default:"24h"
How often Flux checks for chart updates
spec.install.crds
string
default:"Create"
CRD management strategy during installation

IP Address Pool Configuration

MetalLB requires an IPAddressPool resource to define the range of IP addresses it can assign:
ippool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.10.0/24

L2 Advertisement

For Layer 2 mode, configure L2Advertisement to announce IP addresses:
l2advertisement.yaml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2-advertisement
  namespace: metallb-system
spec:
  ipAddressPools:
    - first-pool
L2 mode is simpler to configure but requires that MetalLB nodes are on the same Layer 2 network as your clients.

Operating Modes

Layer 2 mode uses ARP (for IPv4) or NDP (for IPv6) to announce service IPs:Advantages:
  • Simple configuration
  • No special network equipment required
  • Works with standard switches
Limitations:
  • All traffic goes through a single node
  • Failover can take a few seconds
Configuration:
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2-advertisement
spec:
  ipAddressPools:
    - first-pool

Using LoadBalancer Services

1

Create a LoadBalancer service

Define a service with type LoadBalancer:
service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: default
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
2

Apply the service

kubectl apply -f service.yaml
3

Verify IP assignment

Check that MetalLB assigned an external IP:
kubectl get svc my-app
Expected output:
NAME     TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)
my-app   LoadBalancer   10.96.100.50    192.168.10.10   80:30123/TCP
4

Test connectivity

Access the service using the external IP:
curl http://192.168.10.10

Advanced Configuration

Requesting Specific IP Addresses

You can request a specific IP from the pool:
apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.10.50  # Request specific IP
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080

IP Sharing

Multiple services can share the same IP using the metallb.universe.tf/allow-shared-ip annotation:
apiVersion: v1
kind: Service
metadata:
  name: http-service
  annotations:
    metallb.universe.tf/allow-shared-ip: "shared-ip-key"
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.10.100
  ports:
    - port: 80
      protocol: TCP
When sharing IPs, ensure services use different ports to avoid conflicts.

Verifying MetalLB Status

Check MetalLB pods

kubectl get pods -n metallb-system
Expected output:
NAME                          READY   STATUS    RESTARTS   AGE
controller-xxxxxxxxxx-xxxxx   1/1     Running   0          5d
speaker-xxxxx                 1/1     Running   0          5d
speaker-yyyyy                 1/1     Running   0          5d

View IP address pools

kubectl get ipaddresspools -n metallb-system

Check allocated IPs

kubectl get svc --all-namespaces -o wide | grep LoadBalancer

Troubleshooting

Service stuck in pending

If a LoadBalancer service remains in pending:
  1. Check if IPAddressPool is configured:
    kubectl get ipaddresspools -n metallb-system
    
  2. Verify pool has available IPs:
    kubectl describe ipaddresspool first-pool -n metallb-system
    
  3. Check MetalLB controller logs:
    kubectl logs -n metallb-system -l app.kubernetes.io/component=controller
    

IP not reachable

  1. Verify L2Advertisement is configured:
    kubectl get l2advertisements -n metallb-system
    
  2. Check speaker logs:
    kubectl logs -n metallb-system -l app.kubernetes.io/component=speaker
    
  3. Verify network connectivity from speaker nodes

Best Practices

  • Define IP pools carefully to avoid conflicts with your network DHCP range
  • Use L2 mode for simplicity in small to medium deployments
  • Consider BGP mode for production environments requiring high availability
  • Monitor IP address pool utilization to prevent exhaustion
  • Document IP assignments for services in your infrastructure documentation

Resources

Build docs developers (and LLMs) love