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:
- Install the FRR plugin: System → Firmware → Plugins → os-frr
- Enable BGP: Routing → BGP → General
BGP AS Number: 65001
Router ID: 192.168.1.1
- Add a neighbor: Routing → BGP → Neighbors
Peer IP: 10.0.0.2
Remote AS: 65002
Description: My BGP Peer
- 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:
- Multiple ISPs with failover requirements
- Multiple autonomous networks (different firewalls, different administrative domains)
- Anycast setups (same IP served from multiple locations)
- You’re studying for network certifications or interviews
- You want to understand how the internet actually works by running it
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.