How to Set Up MetalLB for Load Balancing on a Bare-Metal Kubernetes Cluster

Cloud-hosted Kubernetes clusters get load balancers automatically provisioned by AWS, GCP, or Azure the moment you request a LoadBalancer service. A homelab cluster running on your own hardware — like the Raspberry Pi K3s cluster covered in an earlier guide — has no such cloud provider to call, which is exactly the gap MetalLB for bare-metal Kubernetes fills.

Why Bare-Metal Clusters Need MetalLB

Without a cloud provider’s load balancer integration, a Kubernetes LoadBalancer service request on a self-hosted cluster gets stuck in a permanently “Pending” state, since nothing exists to actually assign it an external IP address. MetalLB solves this by implementing load balancer functionality directly within your own network, assigning real IP addresses from a range you control to services that request one.

How MetalLB Works

MetalLB operates in one of two modes:

  • Layer 2 mode – the simplest option, where MetalLB responds to ARP requests for the assigned IP, making one cluster node responsible for that IP at a time (with automatic failover if that node goes down)
  • BGP mode – integrates with actual network routing hardware supporting BGP, distributing traffic across multiple nodes simultaneously; more complex to set up but offers genuine load distribution rather than single-node responsibility

Most homelab setups start with Layer 2 mode due to its simplicity and lack of dependency on specialized routing hardware.

Installing MetalLB

For a K3s cluster, install MetalLB using its official manifest:

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/main/config/manifests/metallb-native.yaml

Configuring an IP Address Pool

Define a range of IP addresses from your home network that MetalLB can assign to services — chosen carefully to avoid conflicting with your router’s DHCP range:

yaml

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: homelab-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.1.200-192.168.1.220

Apply this configuration:

kubectl apply -f ipaddresspool.yaml

Enabling Layer 2 Advertisement

Create an accompanying L2Advertisement resource so MetalLB actually announces the assigned IPs on your local network:

yaml

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: homelab-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - homelab-pool

Testing with a Sample Service

Deploy a simple test service with type LoadBalancer:

yaml

apiVersion: v1
kind: Service
metadata:
  name: test-service
spec:
  type: LoadBalancer
  selector:
    app: test-app
  ports:
    - port: 80
      targetPort: 80

Check that MetalLB assigns an external IP from your configured pool:

kubectl get svc test-service

Adding an Ingress Controller

While MetalLB assigns IPs to individual services, most homelab clusters run many services and don’t want a separate IP for each one. An Ingress controller (commonly NGINX Ingress or Traefik) sits behind a single MetalLB-assigned IP and routes incoming requests to the correct internal service based on hostname or path, similar in concept to the reverse proxy setup covered in the Nginx Proxy Manager guide, but built specifically for Kubernetes.

Installing NGINX Ingress Controller

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml

Once installed, the Ingress controller’s own service receives a single MetalLB-assigned IP, and individual Ingress resources within your cluster define hostname-based routing rules to different backend services — one external IP handling traffic for many internal applications.

Final Thoughts

Setting up MetalLB for bare-metal Kubernetes closes one of the most noticeable gaps between running Kubernetes on cloud infrastructure versus your own homelab hardware. Combined with an Ingress controller, it gives your Raspberry Pi or Proxmox-based K3s cluster the same practical load balancing and routing capabilities you’d expect from a cloud-hosted cluster, entirely self-contained on your own network.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *