MetalLB: Load Balancing for Bare Metal

Khimananda Oli 9 min read Virtualization
MetalLB: Load Balancing for Bare Metal

By Khimananda Oli | Last reviewed: August 2026

Running Kubernetes on self-managed hardware means you lose the automatic load balancer provisioning that cloud providers handle silently. MetalLB: Load Balancing for bare metal solves this gap by implementing a network load balancer natively within your cluster, assigning real IP addresses from your local subnet to Services of type LoadBalancer. Whether you are running a home lab, an edge deployment in Nepal, or an enterprise on-prem data center, understanding how to configure and troubleshoot MetalLB is essential for making your services accessible without manual NodePort management.

How does MetalLB: Load Balancing for bare metal actually work?

MetalLB is not a traditional load balancer like HAProxy or Nginx that proxies traffic at the application layer. Instead, it is a control-plane component that manipulates network protocols to direct traffic to your nodes. Understanding this distinction prevents most configuration errors I see in production audits. When you create a Service with type: LoadBalancer, MetalLB allocates an IP from a configured pool and announces it to the network so clients can reach it directly.

Upstream RouterNode A + SpeakerNode B + SpeakerService VIPBGP / ARP Announcekube-proxy routes to Pods
MetalLB speakers announce the Service VIP to the network; kube-proxy handles actual packet forwarding to pods.

The system consists of two components: the controller (a Deployment) that manages IP allocation and validates configuration, and the speaker (a DaemonSet) that runs on every node and speaks the network protocol. In Layer 2 mode, the speaker uses ARP (IPv4) or NDP (IPv6) to respond to address resolution requests, effectively telling the network "I am that IP." Only one speaker acts as the leader per service IP to avoid duplicate responses. In BGP mode, all speakers establish peering sessions with your upstream routers and advertise the IP prefix, enabling ECMP (Equal-Cost Multi-Path) routing for true load distribution across nodes.

This architecture means MetalLB does not touch your data path. Once the IP is announced, packets flow through standard Linux networking and Kubernetes ingress controllers or kube-proxy rules. This separation is why MetalLB is so lightweight but also why debugging requires understanding both Kubernetes and your underlying network topology.

How do you install and configure MetalLB in Layer 2 mode?

Layer 2 mode is the default choice for most bare metal deployments because it requires no special router configuration. It works on any flat network where your nodes and clients share the same broadcast domain. For teams deploying K3s on edge hardware or single-subnet environments, this is usually the right starting point.

Install MetalLB via Helm

Helm is the recommended installation method in 2026. The official chart handles CRDs, RBAC, and speaker scheduling correctly:

helm repo add metallb https://metallb.github.io/metallb
helm repo update
helm install metallb metallb/metallb \
  --namespace metallb-system \
  --create-namespace \
  --version 0.14.9

Wait for the speaker pods to reach Ready state before proceeding. Speakers require hostNetwork: true and specific capabilities (CAP_NET_ADMIN, CAP_NET_RAW), so verify they are not blocked by PodSecurityAdmission policies if you have strict enforcement enabled.

Define an IPAddressPool and L2Advertisement

MetalLB v0.13+ separated IP pools from advertisement configuration. Create a pool with IPs from your local subnet that are outside your DHCP range to prevent conflicts:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: production-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.10.200-192.168.10.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2-advert
  namespace: metallb-system
spec:
  ipAddressPools:
    - production-pool

A common mistake is overlapping the pool with existing static IPs or DHCP leases. Always reserve these addresses in your router or DHCP server first. In Nepali ISP environments where static IP blocks are small, coordinate with your network admin to carve out a dedicated range specifically for MetalLB.

When should you use BGP mode instead of Layer 2?

Layer 2 mode has a fundamental limitation: only one node receives traffic for a given service IP. If that node fails, there is a brief outage while ARP caches expire and the new leader announces itself. BGP mode eliminates this single-point-of-failure by advertising the IP from all nodes simultaneously, letting your upstream router distribute traffic via ECMP.

Layer 2 ModeActive Node (Leader)Standby NodesSingle point of failureFailover: 10-30s ARP timeoutBGP ModeNode ANode BNode CECMP RouterAll nodes activeFailover: <1s BGP convergence
Layer 2 relies on a single leader node; BGP enables multi-path routing and sub-second failover.

Choose BGP when you need high availability without client-side retry delays, when your cluster spans multiple subnets, or when you want to integrate with existing enterprise routing infrastructure. The tradeoff is complexity: you must configure BGP peers on your routers (Cisco, Juniper, MikroTik, or FRR-based Linux boxes). For guidance on securing the underlying network, review Ubuntu security hardening since MetalLB speakers run with elevated network privileges.

Configure BGP Peering

Define your BGP peers and associate them with an IP pool. This example assumes your router AS is 64512 and your cluster uses AS 64513:

apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
  name: core-router
  namespace: metallb-system
spec:
  myASN: 64513
  peerASN: 64512
  peerAddress: 192.168.1.1
  passwordSecret:
    name: bgp-secret
    key: password
---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
  name: bgp-advert
  namespace: metallb-system
spec:
  ipAddressPools:
    - production-pool
  peers:
    - core-router

Store BGP passwords in Kubernetes Secrets, never in plaintext manifests. If you are managing secrets across environments, consider proper secrets management practices to avoid leaking credentials during GitOps syncs.

How do you troubleshoot MetalLB when services stay Pending?

The most frequent issue I encounter is Services stuck in <pending> EXTERNAL-IP state. This almost always stems from misconfiguration rather than bugs. Follow this diagnostic sequence:

  1. Verify pool assignment: Run kubectl get ipaddresspools -n metallb-system and confirm the pool exists and has available addresses. Check events with kubectl describe svc <service-name> — MetalLB emits clear events when allocation fails.
  2. Check speaker status: kubectl get pods -n metallb-system should show all speakers Running. CrashLoopBackOff usually indicates missing capabilities or SELinux/AppArmor denials. Check logs with kubectl logs -n metallb-system -l app=metallb -c speaker.
  3. Validate network connectivity: In L2 mode, test ARP from another machine: arping -I eth0 192.168.10.200. No response means the speaker isn't binding to the correct interface. Use the metallb.universe.tf/address-pool annotation to force a specific pool if you have multiple.
  4. Inspect BGP sessions: In BGP mode, check peer status via router CLI or MetalLB metrics. Sessions stuck in Active/Connect state indicate firewall blocks (TCP 179) or ASN mismatches. Enable debug logging temporarily by setting --log-level=debug on the speaker.
  5. Review admission webhooks: MetalLB v0.14+ includes validating webhooks that reject invalid configs. If kubectl apply fails silently, check kubectl get validatingwebhookconfigurations and ensure the webhook service is reachable.

A subtle gotcha in 2026: some CNI plugins (especially Cilium in strict mode) intercept ARP/NDP before MetalLB can respond. If you use Cilium with eBPF, verify that L2 announcements are compatible with your chaining configuration or switch to BGP mode which operates independently of CNI datapath interception.

MetalLB vs alternatives: Which load balancer fits your bare metal cluster?

MetalLB is not the only option for bare metal load balancing. Choosing the wrong tool leads to operational debt. Here is how the main contenders compare in production:

FeatureMetalLBKube-VIPIngress-Nginx + NodePortHAProxy External
Service Type SupportLoadBalancer nativeLoadBalancer + Control PlaneNodePort onlyExternal LB required
BGP SupportYes (full)LimitedNoN/A
ECMP Multi-pathYesNoNoDepends on HW
ComplexityLow-MediumMediumLowHigh
Data Path ProxyNo (announce only)No (announce only)Yes (L7)Yes (L4/L7)
Best ForGeneral purpose LBHA Control Plane + Simple LBDev/Test clustersLegacy compliance
Need LoadBalancer IP?Require BGP/ECMP?YesNoMetalLB (BGP)Production HASimple L2 Enough?YesNoMetalLB (L2)Small Clusters / LabsKube-VIP / Ext LBControl Plane HA
Decision tree for selecting the appropriate bare metal load balancing strategy based on HA and scale requirements.

In practice, MetalLB covers 90% of bare metal use cases. Kube-VIP shines when you also need control plane HA (VIP for API server) and want a single tool. External HAProxy makes sense only when compliance mandates physical appliance separation or when you need advanced L7 features MetalLB doesn't provide. Avoid NodePort-only setups in production — they expose non-standard ports, complicate firewall rules, and make DNS management painful.

Production Checklist for MetalLB on Bare Metal

Before declaring your MetalLB deployment production-ready, verify these items. Skipping them causes outages during upgrades or node maintenance:

  • Reserve IPs externally: Document and reserve your MetalLB pool in your IPAM system, router config, and DHCP server. Duplicate IP assignment is the #1 cause of intermittent connectivity issues.
  • Monitor speaker health: Expose Prometheus metrics (/metrics on port 7472) and alert on metallb_speaker_announced == 0 for critical services. Integrate with your monitoring stack to catch silent failures.
  • Test failover: Regularly cordon/drain nodes and verify IP migration completes within acceptable SLA. In L2 mode, expect 10-30 second disruption; in BGP, sub-second. Document actual recovery times for your SLOs.
  • Pin versions: MetalLB upgrades occasionally change CRD schemas. Always read release notes and test in staging. Use ArgoCD or Flux to manage upgrades declaratively and enable rollback.
  • Secure the namespace: Restrict RBAC in metallb-system. Speakers run privileged — limit who can modify IPAddressPools or BGPPeers. Audit changes via policy-as-code tools like OPA/Gatekeeper.

Making MetalLB Work Reliably in Your Environment

MetalLB: Load Balancing for bare metal transforms self-managed Kubernetes from a networking headache into a platform that behaves like the cloud. Start with Layer 2 for simplicity, graduate to BGP when your availability requirements demand it, and always validate your network assumptions before going live. The key insight is that MetalLB is a protocol announcer, not a proxy — understanding this mental model makes troubleshooting intuitive rather than mysterious.

If you are designing a bare metal or hybrid Kubernetes platform and need help getting MetalLB, networking, or compliance right the first time, reach out to discuss your architecture. Getting the load balancing layer wrong early creates technical debt that compounds with every new service you deploy.

Frequently Asked Questions

MetalLB provides network load balancing for Kubernetes clusters running outside cloud providers. It assigns external IPs to services using L2 or BGP protocols, enabling standard LoadBalancer service types without proprietary cloud integrations or expensive hardware appliances.

Cloud load balancers rely on vendor APIs and managed infrastructure. MetalLB operates entirely within your cluster using standard networking protocols like ARP or BGP, removing vendor lock-in and recurring fees while maintaining compatible Kubernetes service abstractions for bare metal deployments.

Use BGP mode for production environments requiring high availability and ECMP routing. L2 mode suits simpler setups but only supports single-node failover via leader election, making it less suitable for traffic-heavy workloads needing true load distribution across multiple nodes.

Yes, MetalLB integrates with both Calico and Cilium. For BGP mode, configure MetalLB as a BGP peer alongside your CNI's BGP speaker. Avoid conflicting ASN assignments and ensure route advertisements do not overlap between the CNI and MetalLB configurations.

Define IPAddressPool resources specifying CIDR blocks or individual addresses. Reference these pools in L2Advertisement or BGPAdvertisement custom resources. This allows granular control over which services receive addresses from designated subnets based on namespace or label selectors.

Yes, MetalLB v0.14+ supports IPv6 and dual-stack configurations. Create separate or combined IPAddressPool entries for IPv6 ranges. Ensure your underlying network infrastructure, routers, and CNI plugin also support IPv6 before enabling dual-stack service allocation.

Pending status usually indicates exhausted IP pools, misconfigured advertisements, or speaker pod failures. Check kubectl get ipaddresspools for available addresses, verify speaker logs for protocol errors, and confirm that node selectors match nodes where speakers are actually running.

In L2 mode, only one node announces the service IP via ARP at any time. When that node fails, memberlist detects the outage and triggers leader re-election. Another healthy node assumes announcement duties, causing brief connection interruptions during failover typically lasting seconds.

MetalLB itself has minimal attack surface as it only handles IP assignment and protocol announcements. Security depends on proper network segmentation, firewall rules limiting BGP peers, and restricting IPAddressPool allocations. Always validate BGP authentication and monitor for unauthorized route advertisements.

Running multiple instances is unsupported and causes IP conflicts. Instead, use multiple IPAddressPools and advertisement resources within a single MetalLB deployment. Namespace-based pool selection enables logical separation without requiring duplicate controller or speaker components competing for the same addresses.

Controller restarts have no impact on existing service connectivity since data plane forwarding continues uninterrupted. Speaker restarts may cause brief ARP or BGP reconvergence depending on mode. Configure appropriate hold timers and graceful shutdown hooks to minimize disruption during planned maintenance windows.

Verify peer configuration matches router settings including ASN, password, and multihop parameters. Use kubectl exec into speaker pods to run gobgp neighbor commands. Check router-side BGP session state and confirm no ACLs block TCP port 179 between speakers and upstream routers.

No, MetalLB only handles external IP assignment and ingress announcement. Kube-proxy still manages internal ClusterIP routing and DNAT rules. Both components work together where MetalLB attracts external traffic and kube-proxy forwards it to backend pods through existing service mechanisms.

MetalLB v0.15 requires Kubernetes 1.28 or later. Older versions lack required CRD validation and API features. Always check the official compatibility matrix before upgrading, as minor version mismatches can cause silent failures in IP allocation or advertisement processing.

MetalLB adds negligible resource overhead. The controller uses under 50MB RAM and minimal CPU. Speakers consume slightly more in BGP mode due to route processing but typically stay below 100MB RAM. Neither component performs packet forwarding, keeping computational impact very low.