A Routing Protocol Lab with Containerlab and FRRouting

Build a virtual network of routers running real OSPF and BGP, entirely in containers on one machine -- and watch routes actually propagate, fail over, and reconverge.

What you'll build and why

Containerlab wires together container-based network devices into a defined topology from one YAML file; FRRouting is a real, production-grade routing suite (used in actual production networks, not a simplified teaching tool) running inside those containers. This guide builds a small multi-router topology, configures OSPF within it, adds a second autonomous system peered over BGP, and then breaks a link on purpose to watch reconvergence happen -- because routing protocols make much more sense watched live than read about.

Don't build this if: you don't have a specific interest in routing protocols at this level -- most homelab networking (the VLAN and firewall guides elsewhere in this section) doesn't require running your own dynamic routing protocol. This is for understanding how the protocols themselves work, or for a specific use case that actually needs dynamic routing.

How it works

  Containerlab reads topology.yml, spins up FRR-based
router containers, wires virtual links between them
exactly as defined
R1 ---- R2
│        │
│  OSPF  │  (single autonomous system)
│        │
R3 ------┘
│
│  eBGP peering (two AS's exchanging routes)
│
R4  (a separate AS)

Each "router" is a container running FRR's routing daemons (ospfd, bgpd) -- real routing software, just running in containers instead of on dedicated router hardware. The protocols behave exactly as they would on physical gear.

Before you start

Decision: OSPF first, BGP second. OSPF (an interior gateway protocol, routing within one network you control) is the more approachable starting point; BGP (how separate networks/autonomous systems exchange routes, the protocol that holds the actual internet together) has more moving parts. This guide builds OSPF fully working before introducing BGP, rather than both at once.

Steps

Step 1: Install Containerlab

$ curl -sL https://containerlab.dev/setup.sh | sudo bash -s "all"

The official install script. Check containerlab.dev for current alternative install methods (package managers are also supported) if you prefer not to pipe a script to bash.

Step 2: Define the topology

topology.yml:

name: routing-lab
topology:
nodes:
r1:
kind: linux
image: frrouting/frr:latest
r2:
kind: linux
image: frrouting/frr:latest
r3:
kind: linux
image: frrouting/frr:latest
r4:
kind: linux
image: frrouting/frr:latest
links:
- endpoints: ["r1:eth1", "r2:eth1"]
- endpoints: ["r2:eth2", "r3:eth1"]
- endpoints: ["r1:eth2", "r3:eth2"]
- endpoints: ["r3:eth3", "r4:eth1"]

R1-R2-R3 form a triangle (redundant paths, needed for the reconvergence test later); R4 is peered separately for the BGP step.

Step 3: Deploy the lab

$ sudo containerlab deploy -t topology.yml

Expected output: Containerlab reports each node's status and how to reach it, typically via docker exec or SSH depending on the node kind.

Step 4: Configure OSPF on R1, R2, R3

On each, via FRR's vtysh CLI:

$ sudo docker exec -it clab-routing-lab-r1 vtysh
router ospf
network 10.0.12.0/30 area 0
network 10.0.13.0/30 area 0
!

(Adjust the actual subnet per each link's addressing -- assign RFC1918 point-to-point subnets per link when interface-configuring each router, done via interface eth1 / ip address <addr>/<mask> in the same vtysh session before the OSPF config above.) Repeat with the relevant networks on R2 and R3.

Step 5: Verify OSPF neighbors and routes

r1# show ip ospf neighbor
r1# show ip route ospf

Expected output: R1 shows R2 and R3 as full OSPF neighbors, and routes to networks only directly connected to R2/R3 now appear in R1's routing table too -- learned via OSPF, not manually configured.

Step 6: Configure eBGP between R3 and R4 (two autonomous systems)

On R3:

router bgp 65001
neighbor <r4-interface-ip> remote-as 65002
network <a-network-r1-r2-r3-know-about>/24
!

On R4:

router bgp 65002
neighbor <r3-interface-ip> remote-as 65001
network <a-network-only-r4-knows-about>/24
!

Step 7: Verify the BGP peering and route exchange

r3# show bgp summary
r3# show ip route bgp

Expected output: R3 shows R4 as an established BGP neighbor, and R4's advertised network now appears in R3's routing table with the eBGP-learned route source.

Verify it works

  • show ip ospf neighbor on R1, R2, and R3 all show each other as Full neighbors
  • show ip route ospf on R1 includes routes to R3's directly-connected networks, learned dynamically
  • show bgp summary on R3 shows R4's session as Established
  • R4's advertised network is reachable (a ping from R1, whose route to it was learned entirely dynamically through OSPF-then-BGP redistribution/route visibility, not manual configuration)
  • The actual point of this guide -- reconvergence: bring down the R1-R2 link (sudo containerlab tools disconnect or simply shutting the interface within R1's vtysh) while R1-R3-R2 remains available as an alternate path. Confirm OSPF detects the failure and reconverges -- show ip route ospf on R1 should still show a route to R2's networks, now via R3 instead. Time roughly how long this takes; this is OSPF convergence, observed directly rather than described

Secure it

  • This is a lab, not a hardened deployment: BGP in this guide has no authentication (MD5/TCP-AO) configured -- appropriate for an isolated lab, not for anything resembling a real routing deployment where an unauthenticated peer relationship is a real risk (route hijacking, injected bad routes).
  • What's exposed: Containerlab's virtual links are isolated to the lab topology by default -- nothing here is reachable from outside the host unless you deliberately bridge it out.
  • If this ever informs real router configuration: BGP authentication, prefix filtering (only accepting expected routes from a peer), and max-prefix limits are all real production concerns this lab intentionally skips for simplicity -- don't carry this config directly into anything internet-facing.

Back it up and maintain it

What matters: topology.yml and any router configs you've built up -- worth version-controlling if you're iterating on the lab design, though the lab itself is fully reproducible from the file.

Update cadence: Containerlab and the FRR image both update independently; pin a specific FRR image tag if you want a stable, reproducible lab rather than picking up FRR changes on every redeploy.

What to monitor: nothing persistent -- this lab exists only while deployed.

Troubleshooting

Logs: sudo docker logs clab-routing-lab-<node>; within vtysh, show logging for FRR's own routing daemon logs.

Symptom Likely cause Diagnostic Fix
OSPF neighbors never reach Full Interface addressing mismatch, or the network statement's area/subnet doesn't match the actual interface show ip ospf interface to confirm OSPF is actually enabled on the expected interface with the expected area Fix the addressing or network statement to match the real interface config
BGP session stuck in Active/Connect, never Established Wrong neighbor IP, wrong remote-as, or no IP connectivity between the two routers at all ping between the two BGP peer IPs directly, outside of BGP Fix the neighbor IP/AS number, or the underlying link/IP config
Containerlab deploy fails immediately Docker not running, or insufficient permissions sudo systemctl status docker; confirm running as root/with sudo (Containerlab typically requires it) Start Docker; rerun with sudo
Route doesn't reconverge after breaking a link No actual alternate path exists in the topology for that specific route, or OSPF hasn't finished recalculating yet show ip ospf database to confirm the topology as OSPF currently understands it Confirm the topology genuinely has redundancy for that path; allow a few seconds for SPF recalculation
vtysh command not found / can't reach router CLI Wrong container name, or FRR's daemons aren't running inside that node sudo docker exec -it <exact-container-name> vtysh -- confirm exact name via containerlab inspect Use the correct container name from containerlab inspect -t topology.yml

Undo

$ sudo containerlab destroy -t topology.yml

Removes every container and virtual link created for the lab -- fully reversible, nothing persists outside the lab's own deployment.

Go further

  • Add route redistribution between OSPF and BGP (learning OSPF-internal routes into BGP and vice versa) for a more complete picture of how enterprise networks actually connect internal and external routing
  • Introduce a deliberate route filter or prefix-list on the BGP peering and observe how it changes what each side learns
  • Explore Containerlab's support for other network OS images beyond FRR if you want to practice against vendor-specific syntax (Arista, Nokia, and others have supported container images)

Resources

Official documentation:

Source and releases:

Community:

Related DaemonPress projects:


Last verified: 2026-09-21, checked against official Containerlab and FRRouting documentation.