MetalLB is a load balancer for bare-metal Kubernetes clusters, and when combined with Minikube, it allows you to expose your LoadBalancer type services locally without needing an external cloud provider.

Here’s how to set it up and see it in action.

First, ensure you have Minikube running. If you don’t, start it with a driver that supports LoadBalancer services, like docker or virtualbox:

minikube start --driver=docker

Next, we need to enable the MetalLB addon within Minikube. This command downloads and deploys MetalLB’s controller and speaker components into your Minikube cluster:

minikube addons enable metallb

This will output something like:

    ▪ MetalLB is an addon for Minikube.
    ▪ It provides a network load-balancer implementation for bare-metal clusters.
    ▪ Follow the steps below to enable MetalLB.
    ▪
    ▪ Enable MetalLB:
    ▪   minikube addons enable metallb
    ▪
    ▪ To configure MetalLB with your desired IP address pool:
    ▪   1. Get the Minikube node IP:
    ▪      minikube ip
    ▪   2. Create a MetalLB configuration file (e.g., metallb-config.yaml):
    ▪      apiVersion: v1
    ▪      kind: ConfigMap
    ▪      metadata:
    ▪        namespace: metallb-system
    ▪        name: config
    ▪      data:
    ▪        addresses: |
    ▪          192.168.49.50-192.168.49.70 # Replace with your desired IP range
    ▪   3. Apply the configuration:
    ▪      kubectl apply -f metallb-config.yaml
    ▪
    ▪ For more information, run:
    ▪   minikube addons doc metallb

The output tells you the next crucial step: configuring MetalLB’s IP address pool. By default, MetalLB needs a range of IP addresses it can assign to LoadBalancer services. Minikube, when running with the docker driver, typically uses a virtual network with an IP range that you can query.

Let’s get the IP address of your Minikube node. This is the IP that MetalLB will use to serve traffic to your services:

minikube ip

This might output something like 192.168.49.2.

Now, we’ll create a ConfigMap to tell MetalLB which IP addresses it can use. We’ll use a range that’s likely available on your Minikube’s network. A common range for the docker driver is 192.168.49.200-192.168.49.250. Crucially, ensure the IP range you choose does not conflict with your local network’s DHCP server or other static IPs.

Create a file named metallb-config.yaml with the following content, adjusting the IP range as necessary based on your minikube ip output and network.

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  addresses: |
    192.168.49.200-192.168.49.250

Apply this configuration to your cluster:

kubectl apply -f metallb-config.yaml

Now, let’s deploy a simple application and expose it using a LoadBalancer service. We’ll use Nginx as an example.

Create a file named nginx-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

And a file named nginx-service.yaml:

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

Apply these to your cluster:

kubectl apply -f nginx-deployment.yaml
kubectl apply -f nginx-service.yaml

Watch the service status. It will take a moment for MetalLB to assign an IP address.

kubectl get service nginx-service -w

You should see output similar to this:

NAME           TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
nginx-service  LoadBalancer   10.96.179.10   <pending>       80:31234/TCP   15s
nginx-service  LoadBalancer   10.96.179.10   192.168.49.200  80:31234/TCP   30s

Notice how the EXTERNAL-IP changes from <pending> to an IP address from the range you configured in metallb-config.yaml (e.g., 192.168.49.200).

You can now access your Nginx service by navigating to http://192.168.49.200 in your web browser.

The surprising part about MetalLB’s operation is that it doesn’t actually route traffic in the traditional sense. Instead, it acts as a control plane that announces IP addresses via ARP (Address Resolution Protocol) or BGP (Border Gateway Protocol) to the local network. When a packet arrives at the assigned IP address, the Minikube node’s network interface, which is listening for ARP requests for that IP, will respond. The packet is then forwarded to the Kubernetes service on the Minikube node. For Minikube, MetalLB primarily uses ARP announcements.

This means MetalLB is essentially telling your local network, "Hey, I own this IP address," and your network’s switch (or in Minikube’s case, the virtual network’s bridge) directs traffic for that IP to your Minikube node. The actual load balancing and forwarding within the cluster are then handled by kube-proxy or other CNI-specific mechanisms.

The next step is to explore how MetalLB handles more complex configurations, such as IP address pooling strategies and layer 2 vs. BGP mode.

Want structured learning?

Take the full Minikube course →