Kubernetes is great, however with the default K3S setup, it exposes traefik as a nodeport, which is not optimal for High Availibility / Redunancy. As well, the service external IP’s are not routable, and are only accessible from within the cluster. This is where MetalLB comes in, it allows you to expose services as LoadBalancer, and will automatically configure your router to forward traffic to the correct node.

However, the UDM-Pro/SE is lacking on many features….. and this is one of them. The UDM-Pro/SE does not support the use of BGP atleast within the GUI.

Credit to Map59’s Blog Post for some guidance with BGP on the UDM.




First, you’ll want to SSH into your UDM, and run the following commands:

curl -fsL "https://raw.githubusercontent.com/unifi-utilities/unifios-utilities/HEAD/on-boot-script-2.x/remote_install.sh" | /bin/sh

This will install the on-boot-script for the UDM, which will allow us to run commands on boot. This allows the configuration changes we make to survive a software update, as without it the changes would be deleted.

You can checkout their github repo here, they have guidance aswell on how to run things like Pi-Hole etc, natively within the UDM.

FRR Install

Now that we have setup an onboot script, we’re going to create the actual boot script for FRR.

Open /data/on_boot.d/10-frr.sh via vim /data/on_bood.d/10-frr.sh and paste the following:


# If FRR is not installed then install and configure it
if ! command -v /usr/lib/frr/frrinit.sh &> /dev/null; then
    echo "FRR could not be found"
    rm -f /etc/apt/sources.list.d/frr.list
    curl -s https://deb.frrouting.org/frr/keys.asc | sudo apt-key add -
    echo deb https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable | sudo tee -a /etc/apt/sources.list.d/frr.list

    apt-get update && apt-get -y install frr frr-pythontools
    if [ $? -eq 0 ]; then
        echo "Installation successful, updating configuration"
        echo > /etc/frr/vtysh.conf
        rm -f /etc/frr/frr.conf
        chown frr:frr /etc/frr/vtysh.conf
    service frr restart

Now, run this file to install FRR


FRR Configuration

Now, lets enable BGP within FRR, and configure it to talk to our K3S cluster.

Open /etc/frr/daemons via vim /etc/frr/daemons and change bgpd=no to bgpd=yes

Now, open /etc/frr/bgpd.conf via vim /etc/frr/bgpd.conf and paste the following:

! -*- bgp -*-
password zebra
frr defaults traditional
log file stdout
router bgp 65510
 bgp ebgp-requires-policy
 bgp router-id --CHANGE_ME_UDM_IP--
 maximum-paths 1
 ! Peer group for MetalLB
 neighbor ML peer-group
 neighbor ML remote-as 65512
 neighbor ML activate
 neighbor ML soft-reconfiguration inbound
 neighbor ML timers 15 45
 neighbor ML timers connect 15
 ! Neighbors for MetalLB
 neighbor --K3S-NODE-IP-- peer-group ML
 neighbor --K3S-NODE-IP-- peer-group ML
 neighbor --K3S-NODE-IP-- peer-group ML

 address-family ipv4 unicast
  redistribute connected
  neighbor DNS activate
  neighbor DNS route-map ALLOW-ALL in
  neighbor DNS route-map ALLOW-ALL out
  neighbor DNS next-hop-self
  neighbor RP activate
  neighbor RP route-map ALLOW-ALL in
  neighbor RP route-map ALLOW-ALL out
  neighbor RP next-hop-self
route-map ALLOW-ALL permit 10
line vty

You’ll want to edit the bgp router-id to be the internal IP address of your UDM, and the K3S-NODE-IP’s to be the internal IP’s of your K3S nodes reachable from the UDM.

MetalLB Setup

Now that we have BGP setup on the UDM, we can install & configure MetalLB to use it.

K3S Disable

By default, k3s ships with the ServiceLB LoadBalancer, which is what we are replacing MetalLB with. However before we install MetalLB we’ll need to remove it from our cluster.

We can remove this by editing the following file /etc/systemd/system/k3s.service and adding --disable servicelb to the ExecStart line.

It should look something like this:

ExecStart=/usr/local/bin/k3s \
    server --disable servicelb \

MetalLB Install

First, we’ll need to install MetalLB, you can do this by running the following command:

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.9/config/manifests/metallb-frr.yaml

MetalLB Configuration

Now, we’ll need to configure MetalLB with the IP Range we want it to use, as well as the BGP Information for our UDM.

To start, lets setup the IP-Range, you’ll want to create a yaml file with the following contents: Replace the addresses with the IP Range you want to use.

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
  name: first-pool
  namespace: metallb-system

Now apply this to your cluster via kubectl apply -f <filename>

Now, we’ll need to setup the BGP information for MetalLB, you’ll want to create a yaml file with the following contents: Replace the peerAddress with the internal IP of your UDM and the ASN numbers if you had changed them in the FRR config.

apiVersion: metallb.io/v1beta2
kind: BGPPeer
  name: udm
  namespace: metallb-system
  myASN: 65512
  peerASN: 65510

And we can apply this just like before with kubectl apply -f <filename>

Lastly, we’ll need to tell Metal to advertise our prefixes with one last yaml file:

apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
  name: advert
  namespace: metallb-system

Once again, apply this with kubectl apply -f <filename>


Now, your UDM should be reciving the routes from your Cluster, and the LB ip’s should be reachable from your UDM/Network. You can verify this by running vtysh -c 'show ip bgp' on your UDM, and you should see entries with the External IP’s of your services. Which will look something like the following.

BGP table version is 9, local router ID is, vrf id 0
Default local pref 100, local AS 65510
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *              0             0 65512 i
 *>                      0             0 65512 i
 *>              0             0 65512 i
 *              0             0 65512 i
 *>                      0             0 65512 i