MetalLB keeps configurations valid through a validating webhook run by the controller. This episode covers how the admission controller works, configuration error handling, CRD migration from v1beta1 to v1beta2, and best practices for upgrading between MetalLB versions.

One source of frustration when using MetalLB is a configuration that looks correct but is silently rejected. The good news: MetalLB has a defensive layer — the validating admission controller run by metallb-controller. Every IPAddressPool, BGPPeer, and advertisement you apply is validated before it's actually stored in the cluster.
Episode 10 dissects this mechanism: how the webhook works, how to read rejected errors, how MetalLB CRDs evolved from v1beta1 to v1beta2, and best practices for upgrading MetalLB without breaking existing configurations.
When you run kubectl apply on a MetalLB resource, the request is forwarded to the Kubernetes API server. Before storing it, the API server calls the validating webhook run by the controller. This webhook checks whether the configuration is valid — for example, whether the IP ranges are well-formed, whether the ASN is in a valid range, and whether references between resources are consistent.
If valid, the resource is stored. If not, the request is rejected and you receive an error. You can see this webhook in the cluster:
kubectl get validatingwebhookconfiguration
kubectl get validatingwebhookconfiguration metallb-webhook-configuration -o yamlkubectl get validatingwebhookconfiguration metallb-webhook-configuration shows MetalLB's built-in webhook, complete with the resources it validates and the mutating rules in effect.
To feel how error handling works, send a deliberately invalid configuration — for example, a pool with an invalid range format:
kubectl apply -f - <<EOF
apiVersion: metallb.io/v1beta2
kind: IPAddressPool
metadata:
name: broken-pool
namespace: metallb-system
spec:
addresses:
- not-an-ip
EOFThe API server will reject it with a message like The IPAddressPool "broken-pool" is invalid along with the rejection details from the webhook. Errors like this save you from broken configurations that could trigger strange behavior on the network.
The webhook's rejection message usually explains which field is problematic and why. Examples of common error patterns:
addresses: not-an-ip is rejected because it isn't a valid IP.myASN or peerASN must be within the valid BGP range (1-4294967295).kubectl get ipaddresspool -A
kubectl describe ipaddresspool broken-poolkubectl describe ipaddresspool broken-pool on a resource that was stored at some point shows the status and conditions explaining why the resource isn't functioning normally.
Besides format, the webhook validates reference consistency. For example, an L2Advertisement referencing a pool with a misspelled name will show a status indicating the pool wasn't found. Note that MetalLB deliberately allows references to resources that haven't been created yet, so the apply order isn't rigid — but the status will flag it if the resource never appears.
MetalLB configuration used to be a ConfigMap, then moved to CRD v1beta1 (starting v0.13), and now uses v1beta2 as the default and stable version (starting v0.14, refined in v0.16). Note that IPAddressPool uses metallb.io/v1beta2 while advertisements and peers can still be written in v1beta1:
kubectl get crd | grep metallb.iokubectl get crd | grep metallb.io lists all of MetalLB's Custom Resources. The version column shows v1beta1 and v1beta2 as served versions — both remain active so old manifests can still be applied.
When MetalLB adds a new CRD version, the old version is generally kept served for a while so configurations don't break. Best practices when migrating:
kubectl apply on a staging cluster first.kubectl get before removing the old API version.MetalLB upgrades rarely go smoothly without preparation. These steps reduce risk:
helm list -n metallb-system
kubectl get pods -n metallb-system
helm search repo metallb/metallbhelm search repo metallb/metallb shows the latest available chart version. Before bumping versions, always compare the changes between old and new — especially CRD changes, because an upgrade that forces API version conversion is the most common source of errors.
helm upgrade metallb metallb/metallb --namespace metallb-system --version 0.16.1
kubectl get pods -n metallb-systemhelm upgrade metallb metallb/metallb --version 0.16.1 pulls in all resource changes at once. After it finishes, kubectl get pods -n metallb-system confirms all components are healthy again.
Episode 10 completes the validation and versioning mechanism: the controller webhook validates every configuration before it's stored, errors are rejected with readable messages, CRDs evolved from v1beta1 to v1beta2, and upgrades are done with careful steps.
Key takeaways:
metallb-controller runs the validating webhook for all MetalLB resources.kubectl get validatingwebhookconfiguration to make sure the webhook is active.In the next episode, episode 11, we'll discuss observability & monitoring — Prometheus metrics from the speaker and controller, analyzing events and logs for diagnosis, and using Grafana dashboards to visually monitor IP allocation and BGP peering status.