Skip to content
SumGuy's Ramblings
Go back

BGP in Your Home Lab: Dynamic Routing for People Who've Run Out of Static Routes

Static Routes Are Fine Until They’re Not

You started with a router and a few devices. Then you added a VLAN. Then a VM host with its own subnets. Then a VPN. Then a second router for a different ISP. At some point, you’re maintaining a spreadsheet of static routes across five devices, and when something changes, you’re SSHing into four boxes and hoping you got the order right.

Dynamic routing protocols exist to make this not terrible. BGP (Border Gateway Protocol) is the one that runs the actual internet — every ISP, cloud provider, and major network uses it to exchange routing information. It’s overkill for most home labs. But “overkill” is kind of the point of a home lab, and understanding BGP at a hands-on level is valuable knowledge.

Here’s when BGP actually makes sense at home, and how to set it up.

iBGP vs eBGP: The 60-Second Version

BGP has two modes based on whether the peers share the same AS (Autonomous System) number:

eBGP (External BGP): Peers with different AS numbers. Used between organizations. Routes between ISPs, between your router and a cloud provider, between your home network and a VPN endpoint. Default TTL is 1 — peers must be directly connected.

iBGP (Internal BGP): Peers with the same AS number. Used within an organization to distribute BGP routes internally. Used by ISPs to distribute external routes to all their routers. Requires full mesh or route reflectors.

For a home lab multi-ISP setup or if you’re experimenting with anycast, you mostly care about eBGP. For distributing routes between multiple routers in your home network, iBGP comes into play.

Your Home AS: 65001 (use 64512-65534 for private/lab AS numbers)
ISP1 AS: 65100
ISP2 AS: 65200

Private AS numbers (64512–65534) exist specifically for lab use and internal setups — like RFC1918 for IP addresses.

FRRouting: BGP for Normal Humans

FRRouting (FRR) is the go-to open-source routing suite. It’s a fork of Quagga with active development, supports BGP, OSPF, IS-IS, EIGRP, and more. Used in production by some serious networks.

# Install on Debian/Ubuntu
sudo apt update
sudo apt install frr frr-pythontools

# Enable BGP daemon (edit daemons file)
sudo nano /etc/frr/daemons
# /etc/frr/daemons — enable what you need
bgpd=yes
ospfd=no
ospf6d=no
ripd=no
sudo systemctl enable --now frr
sudo systemctl status frr

# Access the FRR CLI (vtysh — Cisco-like syntax)
sudo vtysh

Basic BGP Configuration

FRR uses a Cisco-like CLI. Here’s a minimal BGP setup:

# Inside vtysh
configure terminal

! Set your router's ID (usually the loopback or main IP)
router-id 192.168.1.10

! Configure BGP with your AS number
router bgp 65001

 ! Define a BGP neighbor (eBGP peer — different AS)
 neighbor 192.168.1.1 remote-as 65100
 neighbor 192.168.1.1 description "OPNsense Router"

 ! Address family for IPv4 unicast
 address-family ipv4 unicast
  ! Advertise a network you own
  network 10.10.0.0/24
  
  ! Accept routes from this neighbor
  neighbor 192.168.1.1 activate
  
  ! Optional: set a next-hop for ibgp
  ! neighbor 192.168.1.1 next-hop-self
 exit-address-family

exit
end

! Save config
write memory

Verify it’s working:

show bgp summary
show bgp ipv4 unicast
show ip bgp neighbors 192.168.1.1

Peering with OPNsense

OPNsense has FRR available as a plugin — great for home lab BGP without setting up a separate router:

  1. Install the FRR plugin: System → Firmware → Plugins → os-frr
  2. Enable BGP: Routing → BGP → General
BGP AS Number: 65001
Router ID: 192.168.1.1
  1. Add a neighbor: Routing → BGP → Neighbors
Peer IP: 10.0.0.2
Remote AS: 65002
Description: My BGP Peer
  1. Add networks to advertise: Routing → BGP → Networks

From a Linux machine peering with OPNsense:

# vtysh on the Linux side
configure terminal
router bgp 65002
 neighbor 192.168.1.1 remote-as 65001
 address-family ipv4 unicast
  neighbor 192.168.1.1 activate
  network 10.20.0.0/24
 exit-address-family
end
write memory

Check the peer status:

sudo vtysh -c "show bgp summary"
# Look for "Established" state under your peer

Route Filtering: Don’t Advertise What You Shouldn’t

Without filtering, BGP will happily advertise any route in your table to peers. This is fine in a home lab but terrible in production. Learn good habits now:

# Prefix list — allow only specific prefixes out
ip prefix-list MY-ROUTES seq 10 permit 10.10.0.0/24
ip prefix-list MY-ROUTES seq 20 permit 10.20.0.0/24
ip prefix-list MY-ROUTES seq 30 deny 0.0.0.0/0 le 32

# Apply to a neighbor
router bgp 65001
 address-family ipv4 unicast
  neighbor 192.168.1.1 prefix-list MY-ROUTES out
 exit-address-family
# Route map for more complex filtering/manipulation
route-map FILTER-IN permit 10
 match ip address prefix-list ALLOWED-IN
 set local-preference 200

route-map FILTER-IN deny 20

router bgp 65001
 address-family ipv4 unicast
  neighbor 192.168.1.1 route-map FILTER-IN in
 exit-address-family

A Real Home Lab Topology

Here’s a topology where BGP actually earns its keep:

ISP1 (simulated, AS 65100) ──── OPNsense (AS 65001)

ISP2 (simulated, AS 65200) ────────────┤

                               FRR on Linux VM (AS 65002)

                              Internal subnets: 10.10.x, 10.20.x

OPNsense receives routes from both ISPs via eBGP. It selects the best path based on BGP attributes (AS path length, local preference, MED). When ISP1 goes down, routes via ISP2 automatically take over — no manual static route changes.

The FRR Linux VM learns these routes via iBGP (or eBGP if you prefer) and installs them in its kernel routing table:

# Check what routes BGP has installed in the kernel
ip route show proto bgp
# Or: ip route show | grep bgp

Bird2: The Other Option

Bird2 (bird.network.cz) is an alternative to FRR. Slightly different config syntax, arguably cleaner for some use cases:

# bird.conf
router id 192.168.1.10;

protocol bgp peer1 {
    local as 65001;
    neighbor 192.168.1.1 as 65100;
    ipv4 {
        import all;
        export where net = 10.10.0.0/24;
    };
}

FRR has the advantage of Cisco-like syntax (if you already know that) and better OPNsense integration. Bird2 is popular in the anycast and IX (Internet Exchange) world. For a home lab, either works — pick whichever syntax you find more readable.

Debugging BGP

# Check neighbor state
sudo vtysh -c "show bgp neighbors 192.168.1.1"
# State should be "Established"

# Common states and meanings:
# Idle: not trying to connect
# Active: trying to connect, no success yet
# Connect: TCP connection in progress
# OpenSent: TCP connected, BGP Open message sent
# OpenConfirm: waiting for keepalive
# Established: working

# Troubleshooting tips:
# 1. Can you ping the neighbor IP?
# 2. Is port 179 (TCP) open?
sudo ss -tlnp | grep 179

# 3. Do AS numbers match what the peer expects?
# 4. Is the router-id set and reachable?

# Enable BGP debug logging (verbose — disable after troubleshooting)
sudo vtysh -c "debug bgp neighbor-events"
sudo vtysh -c "debug bgp updates"

When BGP Is Overkill (Most of the Time)

Honestly? For a standard home lab with one router and one ISP, BGP is a sledgehammer for a thumbtack. OSPF handles internal dynamic routing with less complexity. Static routes work fine for simple topologies.

BGP makes sense when you have:

The last two are completely valid reasons. Running BGP in your home lab and watching routes get exchanged and failover automatically is genuinely satisfying. It demystifies something that sounds intimidating — the “routing protocol that runs the internet” is, at its core, just two machines telling each other what networks they can reach.

Your future self explaining BGP in a job interview will thank your present self for the hands-on reps.


Share this post on:

Previous Post
Authentik vs Authelia: Single Sign-On for Your Home Lab (Without a PhD)
Next Post
Suricata vs Snort: Intrusion Detection for the Paranoid Home Lab Owner