Skip to main content
RunBook Academy

VyOSXXXII · BFDBFD

BFD with static routes — tracking a static next-hop, and the distance that makes it a fallback

Advanced⏱ ~24 minset protocols static route next-hop bfd profileset protocols bfd profileshow ip routeshow bfd peersshow configuration commands | match staticvtysh -c 'show running-config'journalctl -u frr

What you'll learn

  • Enable BFD tracking on a static next-hop, with a profile built from the 1.4+ interval leaves
  • Explain how BFD removes the route from the RIB when the next-hop fails
  • Build a fallback pattern where the static route is installed only while BFD holds the next-hop up, and set the distance that keeps it a fallback
  • Validate the static+BFD integration with show ip route
  • Recognise the production failure modes — BFD flap, static route stuck installed, asymmetric BFD

Prerequisites

Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling) · 2026-08-19

Not yet marked complete on this device.

Static routes do not have their own failure detection mechanism. A static route to 192.0.2.2 via interface eth0 remains in the RIB even when the next-hop is unreachable. The kernel tries to ARP for 192.0.2.2, fails, and the traffic is dropped silently. BFD solves this by tracking the next-hop’s reachability and removing the route from the RIB when the next-hop fails. When BFD and static routing are integrated, the static route is withdrawn from the RIB within a few hundred milliseconds of the failure — how few depends entirely on the timers you configure — and the local router falls back to whatever else is available, or drops the traffic visibly rather than blackholing it.

This lesson walks the static route + BFD integration on VyOS 1.5 LTS: how to enable BFD tracking on a static next-hop, how staticd withdraws the route when BFD goes down, how to build the fallback pattern so the route is installed only while BFD holds the next-hop up — including the administrative distance without which the “fallback” quietly becomes the primary — and the operational commands that actually exist on the box. The goal is the ability to enable BFD on any static route, choose intervals the link can sustain, and prove the integration is live rather than assume it.

Why integrate BFD with static routes

Static routes are the simplest routing primitive but also the most fragile. A static route to 192.0.2.2 via interface eth0 remains in the RIB until the operator removes it. If the next-hop becomes unreachable:

  • The kernel continues to ARP for the next-hop.
  • The ARP requests are unanswered.
  • The traffic is dropped silently.
  • The operator sees traffic blackholed but the route appears in show ip route.

BFD solves this. With BFD tracking, the static route is removed from the RIB when the next-hop becomes unreachable. The local router can use an alternative route (if one exists) or drop the traffic explicitly.

The canonical use case for static+BFD is a fallback route: the operator has a primary route via a routing protocol and a fallback static route. BFD tracks the static route’s next-hop; when the next-hop fails, BFD removes the static route from the RIB, and traffic falls back to whatever else is available. Two mechanisms have to be right for this to work, and they are independent of each other — BFD decides whether the route is installed, and administrative distance decides whether it is selected. Getting the first right and the second wrong is the subject of a warning further down, and it is the commonest way this design fails.

flowchart LR
  A[Router A] -->|BFD| B[Next-hop 192.0.2.2]
  A -->|BGP/OSPF primary| P[Primary route]
  A -->|static fallback| F[Fallback route]
  F -.->|"BFD: next-hop reachable"| INSTALL[Route installed in RIB]
  F -.->|"BFD: next-hop unreachable"| REMOVE[Route removed from RIB]

  subgraph "RIB"
    P
    INSTALL
    REMOVE
  end

The diagram shows the fallback pattern. The fallback static route is installed in the RIB only when BFD confirms the next-hop is reachable. When BFD detects a failure, the static route is removed, and the primary route is used exclusively.

Enabling BFD on a static route

BFD tracking is a node on the next-hop, not on the route. That distinction matters as soon as a route has more than one next-hop: each next-hop is tracked, or not, on its own.

set protocols static route 198.51.100.0/24 next-hop 192.0.2.2 bfd

That enables tracking with FRR’s built-in BFD defaults. In production you almost always want a named profile instead, so that every route using the same class of link gets the same timers and one edit changes them all:

configure

set protocols bfd profile FAST interval receive '50'
set protocols bfd profile FAST interval transmit '50'
set protocols bfd profile FAST interval multiplier '3'

set protocols static route 198.51.100.0/24 next-hop 192.0.2.2 bfd profile 'FAST'

commit
save

For a next-hop that is not on a directly connected subnet, BFD has to be told so, because single-hop and multi-hop BFD are different protocols on different ports (UDP 3784 and UDP 4784) with different demultiplexing rules:

set protocols static route 198.51.100.0/24 next-hop 10.20.0.9 bfd multi-hop source 10.10.0.1 profile 'FAST'

The source is required for the multi-hop form because the far end identifies the session by the address pair rather than by the incoming interface.

Conditional installation — and why it is not conditional advertisement

Conditional installation is the canonical use case for static+BFD: a primary route learned from a routing protocol, and a static fallback that must not be used unless its own next-hop is genuinely alive.

configure

set protocols bfd profile FAST interval receive '50'
set protocols bfd profile FAST interval transmit '50'
set protocols bfd profile FAST interval multiplier '3'

set protocols bgp system-as '64512'
set protocols bgp neighbor 192.0.2.2 remote-as '65001'
set protocols bgp neighbor 192.0.2.2 bfd profile 'FAST'

set protocols static route 0.0.0.0/0 next-hop 192.0.2.2 bfd profile 'FAST'
set protocols static route 0.0.0.0/0 next-hop 192.0.2.2 distance '200'

set protocols static route 0.0.0.0/0 next-hop 192.0.2.3 distance '250'

commit
save

With the distances set as above:

  • BGP up. The BGP route (distance 20) is selected. The static routes are in the RIB as candidates and are not used.
  • BGP down, 192.0.2.2 still answering BFD. The distance-200 static route is selected. This is the case BFD is protecting: without it the route would be selected whether or not 192.0.2.2 was alive.
  • BGP down and 192.0.2.2 not answering BFD. BFD withdraws the distance-200 route and the distance-250 route via 192.0.2.3 is selected.
  • Everything down. No default route. That is a deliberate outcome, not a failure of the design — blackholing at the router is preferable to sending traffic at a next-hop that is not there, because it fails visibly.

How the configuration is applied

The VyOS static route node generates the FRR static route configuration on commit. The rendered FRR configuration:

ip route 198.51.100.0/24 192.0.2.2 bfd profile FAST
!
bfd
 profile FAST
  receive-interval 50
  transmit-interval 50
  detect-multiplier 3
!

Two things are worth noticing about that render. The static route becomes one ip route line with the BFD clause attached — the tracking really is a property of the next-hop, exactly as the VyOS tree said. And the profile’s leaf names change on the way down: VyOS’s interval receive is FRR’s receive-interval. That is why a web search for the FRR spelling returns commands that VyOS rejects, and vice versa.

Confirm the render with:

vtysh -c 'show running-config'

Note that it is show running-config, not show configurationvtysh does not have the latter.

How the result is validated

PREFIX=198.51.100.0/24
NEXTHOP=192.0.2.2

# The routing table, then the one route, then the route to the next-hop itself.
vtysh -c "show ip route"
vtysh -c "show ip route $PREFIX"
vtysh -c "show ip route $NEXTHOP"

# The BFD sessions and their negotiated timers.
vtysh -c "show bfd peers"
vtysh -c "show bfd peers counters"

# The configuration as VyOS holds it.
journalctl -u frr --since '10 min ago'

The expected output when the session is up:

S>* 198.51.100.0/24 [200/0] via 192.0.2.2, eth0, weight 1, 00:00:01

Read the two numbers in brackets: 200 is the administrative distance — confirming the fallback distance from the previous section actually took — and 0 is the metric. The > is the marker that says this route is the selected one; a route present without > is a candidate that lost.

When BFD reports the next-hop unreachable, the route is not merely deselected, it is withdrawn by staticd and the line disappears entirely. That is the distinction to check for: a route still listed without > means something else won on distance, while a route that is gone from the output means BFD removed it.

Confirm which of the two happened by pairing the route view with vtysh -c 'show bfd peers' — a peer in down state alongside a missing route is BFD doing its job.

How it fails

The production failure modes the engineer must recognise:

  • Static route stays in RIB after the next-hop fails. BFD was never attached, or was attached to a different next-hop of the same route. show configuration commands | match "next-hop" and check that the bfd node hangs off the next-hop you think it does — the node is per-next-hop, so a two-next-hop route can be half-tracked.
  • The route is installed but never used, because something better wins. BFD is doing its job; administrative distance is the deciding factor and the static route lost it. Read the > marker in show ip route, not the presence of the line.
  • The route always wins, even when it was meant to be a fallback. The mirror of the above: distance was left at the default 1, which beats every dynamic protocol. This is the failure the warning above exists for.
  • BFD never leaves down and the route is never installed. One-sided BFD. The far end is not configured, does not support it, or something between them drops UDP 3784 (single-hop) or 4784 (multi-hop). show bfd peers on both ends is the only check that distinguishes this from a link problem, because from the local side “the peer is not answering” looks identical either way.
  • BFD flaps and takes the route with it. The interval is faster than the path can sustain — common on VPN tunnels, wireless, and anything where the control plane is not on dedicated hardware. Raise interval receive and interval transmit, or raise interval multiplier so more consecutive losses are tolerated before the session is declared down. A flapping BFD session is worse than none, because it converts a stable degraded path into an unstable one.
  • A single-hop profile pointed at a next-hop that is more than one hop away. The session never establishes, because single-hop BFD sets TTL 255 and expects it back unchanged. Use bfd multi-hop source ... profile ... for those.

Rollback

Static+BFD changes are configuration changes. The standard rollback paths:

  • delete protocols static route 198.51.100.0/24 next-hop 192.0.2.2 bfd then commit — this stops the tracking and leaves the route permanently installed, which is the pre-BFD behaviour and is the right emergency move when BFD itself is what is flapping.
  • Do not reach for rollback N: the VyOS documentation states that it applies the stored revision by rebooting the router, which on a box carrying BGP sessions is a far larger event than the change you are undoing. Use load from /config/archive/ with compare and commit for a whole-configuration revert.
  • delete protocols bfd profile FAST removes the profile — but only once nothing references it. A profile still named by a static next-hop or a BGP neighbour cannot be deleted, and the commit will say so, which is the CLI saving you from a silent behaviour change.
  • delete protocols static route 198.51.100.0/24 next-hop 192.0.2.2 distance returns that next-hop to the default distance of 1. Know that this is a rollback which re-introduces the fallback-becomes-primary defect, and do it deliberately rather than as a tidy-up.

One property of this rollback is worth stating: removing BFD does not put the route back if it is currently withdrawn — it puts the route back and leaves it there permanently, including when the next-hop is dead. That is the correct emergency move when BFD itself is the problem, and the wrong permanent state. Put it back under a ticket.

The canonical deployment pattern: enable BFD on one route, prove the withdrawal actually happens by blocking BFD to the next-hop, then roll it out to the rest.

Production discipline

Cross-course references

The Linux course’s XIX-Linux-NetFoundations covers the kernel RIB. The OPNsense course’s XXX-OPNsense-DynamicRouting covers the equivalent FRR static+BFD on the firewall side. The static routing lessons vyos-xii-01-static-routes and vyos-xii-06-static-route-troubleshoot cover the static route configuration. The lesson vyos-xxxii-01-bfd-concept covers the BFD protocol; vyos-xxxii-02-bfd-config covers the BFD configuration. The lessons vyos-xxxii-03-bfd-with-bgp and vyos-xxxii-04-bfd-with-ospf cover the BGP and OSPF integrations.

Quiz

Knowledge check · 4 questions

  1. Q1. An operator wants a BFD profile named FAST with a 50 ms transmit and receive interval and a 3x multiplier, on VyOS 1.5. Which set of commands defines it?

  2. Q2. With a BFD profile of 50 ms receive interval and a multiplier of 3, a tracked static route is withdrawn from the RIB within roughly 150-300 ms of the next-hop failing.

  3. Q3. An operator configures a static route to 198.51.100.0/24 via 192.0.2.2 with BFD tracking (FAST profile, 50ms interval, 3x multiplier). The route is in the RIB. The link to 192.0.2.2 fails. What does the operator expect to see in `show ip route`?

    When the link to 192.0.2.2 fails, BFD detects the failure within 150ms. BFD signals staticd to remove the route. The route is removed from the RIB within ~250ms of the link failure.

  4. Q4. An operator configures a fallback static route via 192.0.2.2 with BFD tracking. The operator also has a primary route via BGP to the same destination. The operator expects the static route to be a fallback (only used when BGP is down). What is the canonical configuration pattern?

    The operator wants the BGP route to be preferred when BGP is up. The static route should be a fallback (only used when BGP is down). The BGP route has administrative distance 20 (default). The static route has administrative distance 1 (default). The static route would be preferred when both are available.

Passing score: 75%. Answers are checked in this browser.