Runbook: Add a Static Route
1 · Prerequisites
Confirm every item is in place before any state change.
- Static routes — the foundation of a routed estate
- Static route options — tag, description, on-link, VRF, disable
- Recursive routing — indirect next-hops, MTU, and RFC 8308 path MTU
- Static route troubleshooting — show ip route, traceroute, looking-glass, asymmetric routing
- Administrative distance — how a router prefers one source over another
- commit-confirm — the rollback safety net for remote changes
- rollback — the in-place revert to a known-good state
2 · Pre-checks
Read-only diagnostic commands. If any of these don't match expected output, stop and investigate further.
- ·
show ip route 10.20.0.0/16run BEFORE the change — whatever it answers today is exactly what a rollback restores, and if it answers with a route, this static will displace it - ·
show ip route 192.0.2.99for the intended next-hop: an indirect route whose next-hop does not resolve is installed inactive and never reaches the FIB - · Checked for more-specific prefixes already in the table. Longest-prefix-match beats administrative distance: a /24 from OSPF wins over your /16 static for everything inside the /24.
- · The distance is decided and written in the ticket:
1for a primary,210for a floating backup that must sit behind OSPF, BGP and IS-IS - ·
show configuration commandschecked forredistribute static. If the estate redistributes statics, the blast radius is every router that accepts the advertisement, not this one. - · The return path is confirmed by the far end's owner or by a test from that end — a static route fixes one direction only
- · Baseline captured:
show ip route,show ip route static,show configuration protocols static,ip route show - · Out-of-band console open, peer reviewer named for the
comparediff, change window end time agreed
3 · Procedure
Execute each step in order. Verify the expected output of a step before moving to the next.
- 1Capture the baseline, and specifically the two "before" answers:
show ip route 10.20.0.0/16andshow ip route 192.0.2.99. The first is the rollback expectation; the second decides whether the route can install at all. - 2Check for more-specific prefixes from any source that already cover part of your prefix, and for a
redistribute staticstatement in the routing configuration. - 3Choose the form:
next-hopplusinterfaceon a shared Ethernet segment,next-hopalone on a transit link where the next-hop resolves recursively,interfacealone only on a point-to-point link. - 4Choose the distance before typing it.
1is a primary and beats every dynamic protocol;210is a floating backup that activates only when the preferred source withdraws. - 5Enter
configureand set the route, with adescriptionthat carries the change reference and the reason. The description is stored in the VyOS tree only, and it is the single piece of documentation that outlives you. - 6Run
compareand have the named reviewer read the diff. A transposed octet in a next-hop produces a route that installs cleanly and forwards into nothing. - 7Apply with
commit-confirm 10if you reach this router over the network, orcommitfrom the console. - 8Cross-check three views:
show configuration protocols staticis what you typed,show ip route staticis what FRR accepted,ip route showis what the kernel will actually forward on. All three must agree. - 9Confirm the route is selected and installed:
show ip route 10.20.0.0/16must showS>*. BareS, orinactive, means the route is in the RIB and forwarding nothing. - 10Test the forward path with
traceroute, and usetraceroute -swith the source address production traffic will really use — the router's own source selection is not the same thing. - 11Test the return path from the far end. Forward-only success is the commonest way this change appears to have failed when it has half succeeded.
- 12Confirm inside the timer,
save, and close the ticket with the before and aftershow ip route 10.20.0.0/16side by side.
4 · Verification
Confirm the procedure actually fixed the problem.
- ✓
show ip route 10.20.0.0/16showsS>*:Sfor static,>for selected,*for present in the FIB - ✓The reported next-hop and the resolved egress interface are both the ones you intended
- ✓
ip route showcontains the prefix withproto static. The kernel FIB is the only view that forwards packets; FRR's RIB is a statement of intent. - ✓The FRR view and the kernel view agree. A route in
show ip routeand absent fromip route showdoes nothing, and traffic falls through to a less-specific route. - ✓Every other prefix in
show ip routeis unchanged from the baseline capture - ✓
traceroutefrom the router leaves by the intended path, andtraceroute -sfrom the production source address takes the same path - ✓Traffic completes in both directions, verified from the far end and not only from this router
- ✓If the route is redistributed, a downstream router shows it arriving with the attributes the policy was supposed to stamp
- ✓
savehas run, so the route survives a reboot
5 · Rollback
If verification fails, undo the procedure in reverse order.
- ↶Before any commit,
exit discardthrows the candidate away and leaves the running configuration untouched - ↶After the commit and before
confirm, do nothing — the commit-confirm timer restores the previous configuration by itself - ↶After
confirm:delete protocols static route 10.20.0.0/16,compare,commit,save - ↶Removing the route does not restore "nothing" — it restores whatever the baseline showed. If that was a default route, traffic keeps flowing to the wrong place and fails somewhere downstream where nobody is watching this change.
- ↶A middle state exists and is often the right one:
set protocols static route 10.20.0.0/16 disablekeeps the route in the configuration but stops it being rendered to FRR, so the intent stays reviewable while the forwarding reverts - ↶If the route was redistributed, removing it withdraws it from every router that accepted it, and that convergence is not instantaneous
- ↶A whole-tree
rollback 1reverts everything else in the candidate too. Runcomparebetweenrollbackandcommit, every time.
6 · Escalation
When the runbook isn't enough, contact:
- · Escalate to network engineering when the prefix is already carried by OSPF or BGP. A distance-1 static overrides them and never withdraws, so the route outlives the topology it was written for.
- · Escalate rather than reach for
on-linkwhen the next-hop does not resolve. That flag makes the kernel install the route regardless of reachability; if the next-hop is wrong, traffic is handed to the wrong segment and dropped there silently. - · Escalate to the far end's owner when the forward path works and the return path does not — that is their change, not another route on this router
- · Escalate to network engineering before committing if the router redistributes static routes into BGP: a local operational fix becomes an estate-wide, and possibly an external, advertisement
- · Escalate when two static routes for the same prefix are proposed at the same distance. That is ECMP load sharing, not a primary and a backup, and the difference only becomes visible when one path fails.
- · Hold rather than force: commit the route with
disableset, name the owner of the blocking item, agree an end time, and leave the forwarding table as it was
A static route is not added to the routing table. It competes for it, and at the default administrative distance of 1 it beats every dynamic source in the table except a connected route. Whatever OSPF or BGP has to say about the prefix stops mattering on this router the moment you commit.
That is usually the point. What makes it a production change rather than a one-liner is the other half of the same property: a static route has no mechanism for noticing that it has become wrong. A dynamic route withdraws when the path behind it fails. A static route does not. It keeps pointing at the same next-hop through the outage, the maintenance, the re-addressing project and the decommission, because nothing in the router’s model of the world can tell it otherwise.
So this runbook front-loads two questions that take about a minute each and prevent most of the ways this goes wrong: what carries this prefix today, and does the next-hop actually resolve.
When to use this runbook
- A prefix has to be reachable from this router and no dynamic protocol carries it here.
- A floating backup path is being installed behind a dynamic primary.
- Traffic for a specific destination is being pinned during a migration, with an end date recorded in the ticket.
When not to use it
- To work around a dynamic protocol that is not converging. A distance-1 static will make the symptom disappear, which is exactly the problem: the cause stays, the static outlives the incident, and the next person finds a routing table that contradicts the design with no record of why.
- For the default route. Same tree, same syntax, entirely different blast radius — the default route is every destination the router does not otherwise know, and it deserves its own change.
- For blackhole or reject routes. Same configuration tree, opposite intent, and the verification is inverted: success is traffic not arriving.
- For source-based decisions. A static route matches on destination only. Choosing a path by source, by port or by mark is policy-based routing.
Blast radius
Locally: every packet this router forwards toward the prefix, for as long as the route exists.
Beyond this router: it depends entirely on whether the estate
redistributes statics. redistribute static in an IGP or in BGP
turns a local operational fix into an advertisement, and the routers
that accept it will forward toward this one on the strength of it.
Read the redistribution policy, not just the redistribute statement —
a prefix-list and a route-map usually decide which statics actually
leave, and “my prefix is not in the list” is a fact to verify rather
than assume.
Inputs
- The prefix, in the exact form it will be written.
- The next-hop, and whether it sits on a connected subnet of this router.
- The form:
next-hopwithinterface,next-hopalone, orinterfacealone. - The distance, decided in advance: 1 or 210.
- A description carrying the change reference and the reason.
- Whether the estate redistributes static routes.
- The owner of the far end, for the return path.
Step 1: Find out what carries the prefix today
show ip route 10.20.0.0/16
show ip route 192.0.2.99
show configuration commands | match redistributeThree answers to the first command, and each one changes the change:
- Nothing carries it. The static is additive here, and the rollback restores nothing. That sounds safe and is worth thinking about: after a rollback, traffic for the prefix falls to the default route and leaves the building instead of failing locally, which is harder to notice.
- Something carries it at a higher distance. Your static will win. Confirm that displacing it is the intent, in the ticket, with the person who owns the dynamic path.
- Something carries a more specific prefix. This is the one that catches people out.
Step 2: Confirm the next-hop resolves
An indirect static route — a next-hop with no interface — does not carry its own egress. The kernel performs a second lookup on the next-hop address, finds a route that reaches it, and uses that route’s interface. If that lookup fails, the route is installed inactive and never enters the FIB.
The show ip route output for the next-hop is what tells you which
form to use:
- The next-hop is on a connected subnet of this router: use the
direct form,
next-hopplusinterface. The interface is explicit and the route stops depending on another route staying correct. - The next-hop is reachable through another route: the indirect form is appropriate, and you accept that this route’s health is now tied to that other route’s.
- The next-hop does not resolve at all: stop. This is not a route you can add yet.
Step 3: Choose the distance before you type it
Distance 1 is a primary: it wins against everything except connected routes. Distance 210 is a floating backup: it sits behind eBGP at 20, OSPF at 110, IS-IS at 115 and iBGP at 200, stays inactive while any of them carries the prefix, and becomes active the moment they withdraw.
The failure mode is quiet in both directions. A backup committed at distance 1 is permanently active and the intended primary is permanently ignored — and everything works, so nobody finds out until the day the static path is the one that breaks. A primary committed at 210 is permanently inactive, and traffic keeps using a path somebody had decided to move off.
And two statics for the same prefix at the same distance are not a primary and a backup at all: they are equal-cost candidates, and the router load-shares between them. If you want a backup, the distances must differ.
Step 4: Commit, with a description
configure
set protocols static route 10.20.0.0/16 next-hop 192.0.2.99 interface eth0
set protocols static route 10.20.0.0/16 description 'CR-1231 dc01-eu transit, review 2026-11-01'
compare
commit-confirm 10
exitThe description is not exported to FRR. It lives in the VyOS
configuration tree and appears in show configuration protocols static, which is precisely where the next operator will be looking
when they find a route nobody remembers. A change reference, the
destination in words, and a review date cost one line and are the
only documentation that reliably survives.
Read the compare output for the next-hop especially. A transposed
octet produces a route that commits cleanly, installs cleanly, and
forwards into nothing — there is no validation step anywhere in the
stack that knows what you meant.
Point of no return. After the commit this router’s forwarding decision for the prefix has changed, and if the route is redistributed, so has every downstream router’s.
Step 5: Cross-check three views
vyos@vyos:~$ show ip route 10.20.0.0/16S>* 10.20.0.0/16 [1/0] via 192.0.2.99, eth0, 00:00:07Illustrative output
S on its own means FRR has the route and has not selected it. The
word inactive means the recursive lookup for the next-hop failed.
Neither forwards a packet.
Then confirm the kernel agrees:
show configuration protocols static
show ip route static
ip route showThree views of one route, and they answer different questions.
show configuration protocols static is what you typed.
show ip route static is what FRR accepted into its RIB.
ip route show is the kernel FIB — the only one of the three that
forwards a packet, where the entry should carry proto static.
Step 6: Test both directions, from a real source
traceroute 10.20.0.5
traceroute -s 192.0.2.1 10.20.0.5The first traceroute leaves with whichever source address the router chooses for itself. That is often not the address production traffic carries, and on a router with several interfaces or a bound management path it can take a different route entirely. The second form pins the source, which is the test that matches reality.
Then test the return path from the far end, or have its owner do it. A static route is one-directional: it tells this router how to reach the destination and says nothing about how the destination reaches back. Forward success with return failure is the commonest way this change appears to have failed when it has in fact half succeeded, and the remaining half is somebody else’s routing table.
Asymmetry is not automatically a fault — return paths differ from forward paths all over the internet. It becomes a fault when something stateful sits in the path and only sees one direction of the conversation, which on a routed estate with firewalls is most of the time.
Only when both directions are proven:
configure
confirm
save
exitRollback
configure
delete protocols static route 10.20.0.0/16
compare
commit
saveThe rollback restores the baseline, and the baseline is the thing to
have written down. Removing the route does not return the prefix to a
neutral state; it returns it to whatever show ip route 10.20.0.0/16
answered before the change. If that was a default route, traffic
still leaves the router — just not where anyone intends, and it fails
somewhere downstream where nobody connects the failure to this
change.
There is a middle state worth knowing about. set protocols static route 10.20.0.0/16 disable keeps the route in the configuration but
stops it being rendered to FRR, so forwarding reverts while the
intent stays visible and reviewable. For a change that is contested
rather than wrong, that is a better place to stop than deletion.
If the route was redistributed, deletion withdraws it from every router that accepted the advertisement. That convergence takes time and is worth watching rather than assuming.
Common patterns
| Symptom | Likely cause | Next check |
|---|---|---|
Route shows S with no > or * | Another source wins the prefix, or a more specific route exists | show ip route for the exact prefix and its supernet |
Route shows inactive | The recursive next-hop lookup failed | show ip route for the next-hop address |
| Route in FRR, absent from the kernel | It never reached the FIB | ip route show, then escalate |
| Route installed, traffic still fails | A more specific route from another source covers the destination | Trace the exact destination, not the prefix |
| Works from the router, fails from hosts | Source address selection differs | traceroute -s with the production source |
| Forward works, nothing comes back | No return route at the far end | The far end’s routing table, via its owner |
| Backup path never activates | Both routes committed at the same distance, so they load-share | show ip route static and compare the distances |
| Route survives a topology change it should not have | Static routes never withdraw | The description and the review date in the ticket |
| Route disappears after a reboot | save never ran | Compare running against the boot configuration |
Escalation
Escalate rather than commit when:
- The prefix is already carried by OSPF or BGP. Overriding a dynamic protocol with a static is a design decision with a long tail, not a routing fix.
- The next-hop does not resolve and
on-linkis being suggested as the way through. - The forward path works and the return path does not. Adding a second route on this router will not fix somebody else’s table.
- The router redistributes static routes into BGP. A local fix becoming an external advertisement is a different conversation with a different set of people in it.
Holding is a real option: commit the route with disable, name the
owner of the blocker, agree an end time, and leave the forwarding
table exactly as you found it.