You Don’t Need Dynamic Routing Until You Do
Static routes are fine. Until you add a second router. Or stand up a k8s cluster that needs to advertise service IPs. Or peer BGP with a friend’s VPS so you can hand off a /29 between ASNs. Then suddenly you’re staring at a blank terminal wondering whether to install FRR or BIRD, and nobody’s written a straight answer.
Here it is.
Both are production-grade, open-source routing daemons that run on Linux. Both support BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, BFD, and more. The real difference is philosophy: FRR is the “I learned on Cisco IOS and I’m not sorry” option. BIRD is the “I want an elegant config file, not a CLI nostalgia trip” option.
Let’s break them down honestly.
What They Are
FRRouting (FRR) is a fork of Quagga (which was itself a fork of GNU Zebra). It’s maintained by the Linux Foundation, used in production by Cumulus Linux, SONiC, and a stack of cloud networking gear. MetalLB’s BGP mode now ships FRR as its backend. Calico supports it. If you’ve deployed anything cloud-native with BGP, you’ve probably interacted with FRR without knowing it.
The defining trait: vtysh. An interactive CLI that makes you feel like you’re SSH’d into a Cisco switch. You can configure it live and write to frr.conf. This is either a feature or a red flag depending on how you feel about that relationship.
BIRD (BIRD Internet Routing Daemon) comes out of Czech Republic — originally the CZ.NIC labs. It’s what Cloudflare runs. It’s what IX operators use for anycast. It’s what you find at the core of a lot of “quietly doing serious work” routing infrastructure. The config is a declarative text file with a small filter language for expressing prefix policies. There’s no interactive CLI. You edit the file, you reload. That’s it.
Config Style: Same BGP Session, Two Approaches
Let’s configure a BGP session to a remote peer — your Hetzner VPS at 5.9.100.1, ASN 65002, you’re AS 65001. You want to advertise a /29 you own (203.0.113.0/29).
FRR
frr version 9.1frr defaults traditionalhostname lab-routerlog syslog informational!router bgp 65001 bgp router-id 10.0.0.1 neighbor 5.9.100.1 remote-as 65002 neighbor 5.9.100.1 description hetzner-vps ! address-family ipv4 unicast network 203.0.113.0/29 neighbor 5.9.100.1 soft-reconfiguration inbound neighbor 5.9.100.1 route-map EXPORT out neighbor 5.9.100.1 route-map IMPORT in exit-address-family!ip prefix-list MY_PREFIXES seq 5 permit 203.0.113.0/29!route-map EXPORT permit 10 match ip address prefix-list MY_PREFIXES!route-map IMPORT deny 10!That’s FRR. It reads like IOS. Route-maps, prefix-lists, address-family blocks — if you’ve ever touched a Cisco router or an NXOS switch, this is muscle memory. The ! comments are even there for the aesthetic. FRR is absolutely leaning into the nostalgia.
To apply this live without a reload, you drop into vtysh and type it in:
sudo vtysh# Then paste or type config interactively# commit writes to frr.confBIRD
router id 10.0.0.1;
protocol device { }
protocol direct { ipv4;}
protocol kernel { ipv4 { export filter { if net ~ [ 203.0.113.0/29 ] then accept; reject; }; };}
filter only_my_prefixes { if net ~ [ 203.0.113.0/29 ] then accept; reject;}
protocol bgp hetzner_vps { description "Hetzner VPS peer"; local 10.0.0.1 as 65001; neighbor 5.9.100.1 as 65002;
ipv4 { import none; export filter only_my_prefixes; };}That’s BIRD. No interactive CLI. Edit the file, run birdc configure to reload. The filter language is BIRD’s actual power — you can write complex prefix policies, community tagging, and next-hop rewrites that would take five route-maps and a prefix-list in FRR. It’s cleaner once you get past the unfamiliarity of a routing config that looks like real code.
OSPF: The Home-Lab Routing Glue
BGP between ASNs is fun, but for most home labs the more common need is OSPF — redistributing routes between VLANs, talking to your router, letting your NAS know where everything is without maintaining a manual routing table.
FRR OSPF
router ospf ospf router-id 10.0.0.1 network 10.0.0.0/24 area 0 network 192.168.10.0/24 area 0 passive-interface eth0!Standard IOS syntax. passive-interface to prevent hellos on user-facing ports. Done.
BIRD OSPF
protocol ospf v2 internal { area 0 { interface "eth1" { type broadcast; }; interface "eth2" { type broadcast; }; interface "eth0" { stub; # passive equivalent }; };}BIRD’s OSPF config is similarly readable. The stub keyword is the passive-interface equivalent. One difference: BIRD is stricter about interface naming — you reference actual Linux interface names directly instead of matching by subnet.
BFD: Fast Failure Detection
Both daemons support BFD (Bidirectional Forwarding Detection) for sub-second neighbor failure detection. In FRR, you enable it per-neighbor:
router bgp 65001 neighbor 5.9.100.1 bfd!bfd peer 5.9.100.1 receive-interval 300 transmit-interval 300 detect-multiplier 3!In BIRD, BFD is a separate protocol block that other protocols reference:
protocol bfd { neighbor 5.9.100.1;}
protocol bgp hetzner_vps { bfd graceful; # rest of config...}Both work. FRR’s BFD is slightly more flexible in multi-hop scenarios; BIRD’s is more explicit. Honestly at home-lab scale you probably won’t hit the edge cases.
Protocol Support: Where They Diverge
Both cover the standard suite: BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIP, RIPng, BFD, Static.
Where they differ:
FRR wins on BGP EVPN. If you’re running VXLAN with BGP EVPN control plane (Cumulus-style, or SONiC), FRR is the right call. BIRD has limited EVPN support; FRR is the obvious choice here for anyone doing overlay networking.
BIRD wins on filter expressiveness. BIRD’s filter language lets you manipulate communities, local-pref, MED, and AS-paths in a single clean block. FRR route-maps work, but they’re more verbose and require juggling prefix-lists, community-lists, and access-lists as separate objects. For IXP-style prefix policy, BIRD is significantly less painful.
FRR has PIM/multicast and MPLS/LDP. If you’re doing multicast routing or playing with MPLS labels, FRR has it. BIRD doesn’t.
BIRD2 added RPKI. Both now support RPKI for BGP origin validation — FRR via rpki daemon, BIRD natively. Both integrate with Routinator or GoRTR.
Real Home-Lab Use Case: MetalLB BGP Cluster
You have a k3s cluster. You want MetalLB to advertise service IPs via BGP to your upstream router so pods are reachable without NodePort hacks. This is the use case where FRR wins by default.
MetalLB’s FRR mode (which replaced its old native BGP implementation) means MetalLB manages FRR config automatically via frr.conf inside the pods. You configure the MetalLB BGPPeer CRD, it configures FRR for you, FRR peers with your router.
# MetalLB generates something like this automatically:router bgp 65002 bgp router-id 192.168.10.100 neighbor 192.168.10.1 remote-as 65001 ! address-family ipv4 unicast neighbor 192.168.10.1 activate network 10.96.50.0/24 exit-address-family!Your router (running FRR or BIRD) receives those advertisements and installs routes to your service subnet. From there, any machine on the network can reach your k8s services by IP, no port-forward required.
If your router runs BIRD, MetalLB’s FRR pods still work — BIRD just needs to accept the BGP session:
protocol bgp k8s_node1 { description "MetalLB node"; local 192.168.10.1 as 65001; neighbor 192.168.10.100 as 65002;
ipv4 { import filter { if net ~ [ 10.96.0.0/16+ ] then accept; reject; }; export none; };}Works fine. BIRD as the upstream router, FRR inside MetalLB pods. Mix and match.
Operability: Day-2 Differences
This is where the philosophy gap is most visible.
FRR vtysh gives you a live router CLI. You can type show bgp summary, show ip ospf neighbor, debug bgp updates — same as you’d do on a Cisco. It’s familiar if you’ve done any network engineering work. It’s also a footgun: you can make live changes that don’t survive a restart if you forget to write memory. The config can drift between what’s running and what’s on disk.
BIRD has birdc, a control socket client. You run birdc show protocols, birdc show route, birdc show route export bgp_peer. It’s not a config CLI — it’s read-only status. To change anything, edit bird.conf and birdc configure. There is no way to accidentally create a transient running config that doesn’t match the file. Either it’s in the file or it doesn’t exist.
For home-lab use, both are fine. For production deployments where “what is the router actually doing vs. what do the config files say” is a real audit concern, BIRD’s model is arguably safer.
Performance
BIRD has a longstanding reputation for being lighter and faster under high prefix loads — which is why Cloudflare and IX operators reach for it. At full internet table (900k+ routes), the difference is measurable.
In your home lab, you will never notice. You’re not running a full feed. Even with a hundred OSPF routes and a few BGP peers, both daemons will sit at essentially zero CPU. The performance argument is irrelevant unless you’re literally running an IXP out of your basement, in which case, respect.
Installation
Both are in standard repos:
# FRR (Debian/Ubuntu)sudo apt install frrsudo systemctl enable --now frr
# Enable specific daemons in /etc/frr/daemons:# bgpd=yes# ospfd=yes# bfdd=yes
# BIRDsudo apt install bird2sudo systemctl enable --now birdFRR splits into per-protocol daemons (bgpd, ospfd, isisd, etc.) that you enable individually. BIRD is a single binary — all protocols are compiled in, you just configure or ignore them.
Verdict
Use FRR if:
- You want Cisco-style CLI (
vtysh) and muscle memory applies - You’re running MetalLB BGP mode (it literally uses FRR internally)
- You need BGP EVPN / VXLAN overlay control plane
- You want PIM multicast or MPLS support
Use BIRD if:
- You want a clean, declarative config with no drift risk
- You’re doing complex prefix filtering and BGP policy (the filter language earns its keep)
- You’re peering with an IXP or running anycast
- You want a smaller binary and no daemon proliferation
Use neither if you have fewer than three routers and your traffic doesn’t change topology. Static routes and a ip route add script will serve you fine and you’ll thank yourself at 2 AM when you’re not debugging OSPF DR election.
Both tools are genuinely good. The choice is mostly about whether you want your routing config to look like a Cisco config or like code. Pick whichever one you’ll actually maintain.