VyOSIII · VyOS ArchitectureArchitecture
Generated runtime configuration — what VyOS hands to FRR and the kernel
What you'll learn
- Describe the generated FRR configuration that VyOS produces
- Explain the mapping from the VyOS configuration tree to FRR vtysh commands
- Read the generated FRR config to understand what the routing engine is doing
- Recognise the gap between VyOS configuration and FRR configuration when it occurs
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)
The VyOS configuration tree is the source of truth for what the operator wants the router to do. The generated runtime configuration is the source of truth for what FRRouting and the kernel are actually doing. The two should agree; when they disagree, the operator has a routing incident.
This lesson is the operator’s foundation in the generated runtime configuration: how the VyOS tree translates into FRR vtysh commands, what the generated FRR config looks like, and how to read it.
The translation chain
flowchart LR
V["VyOS configuration tree\n(config.boot)"] --> C["configd\n(translator)"]
C -->|"vtysh -c"| F["FRR running-config\n(read with vtysh)"]
C -->|"ip, nft, tc, etc."| K["Kernel state"]
F -->|zebra netlink| K
The translation chain has two stages:
- VyOS configd → FRR running-config — for routing-protocol configurations, VyOS renders the tree into the equivalent vtysh commands and applies them to FRR.
- FRR running-config → kernel state — FRR itself takes the running config, exchanges messages with peers, computes best paths, and writes the routes to the kernel via zebra.
The operator who understands the first stage can read both views. The operator who understands the second stage can diagnose why FRRouting knows a route the kernel does not (zebra issue) or vice versa (kernel issue).
Reading the generated FRR config
The operator views the generated FRR configuration with:
# Show the full FRR running-config
vtysh -c 'show running-config'
# Narrow the view to one daemon
vtysh -d bgpd -c 'show running-config'
vtysh -d ospfd -c 'show running-config'
# Take out one stanza
vtysh -c 'show running-config' | sed -n '/^router bgp /,/^!/p'
show running-config prints the whole configuration. It takes
no per-protocol argument, so the two ways to narrow it are
vtysh -d <daemon>, which asks a single daemon, and sed,
which cuts the stanza you want out of the full text.
The generated FRR config contains:
interface ...— Linux interface names mapped to FRR’s view.router bgp <asn>— BGP configuration.router ospf ...— OSPF configuration.ip prefix-list ...— prefix lists.route-map ...— route maps.access-list ...— access lists.
Each VyOS configuration tree section has a corresponding FRR configuration section. The translation rules are documented in the VyOS source code; the operator who knows them can predict the generated config from the tree.
Translation rules: VyOS to FRR
For each VyOS tree section, here is the translation:
# VyOS: local AS and router ID
set protocols bgp system-as 65000
set protocols bgp parameters router-id 192.0.2.1
# Generated FRR:
router bgp 65000
bgp router-id 192.0.2.1
# VyOS: BGP neighbour
set protocols bgp system-as 65000
set protocols bgp neighbor 198.51.100.1 remote-as 65001
# Generated FRR:
router bgp 65000
neighbor 198.51.100.1 remote-as 65001
# VyOS: OSPF area and network
set protocols ospf area 0 network 192.0.2.0/24
# Generated FRR:
router ospf
network 192.0.2.0/24 area 0
The BGP pair is worth reading twice, because the VyOS side of
it changed shape in 1.4 (sagitta) and 1.5 (circinus) kept the
new form. The local AS is its own leaf, system-as, rather
than a node the rest of the BGP tree hangs off, and peers sit
at set protocols bgp neighbor ... rather than under the AS
number. The generated FRR side did not change: FRR has always
written router bgp 65000. That is the first thing this
lesson buys you — a VyOS tree that was restructured under a
routing engine that was not, so the two views stopped looking
alike even though the router behaves the same.
OSPF did not move: set protocols ospf area 0 network ...
renders to FRR’s network ... area 0 on 1.5 exactly as it did
before.
For these leaves the mapping is close to one-to-one, and the operator who has read the generated FRR config once can read it again. It is not one-to-one everywhere — see the firewall below.
Translation rules: VyOS to kernel
For configurations no routing daemon owns, the translation goes straight to the kernel:
# VyOS: interface address
set interfaces ethernet eth0 address 192.0.2.50/24
# Reaches the kernel as the equivalent of:
ip addr add 192.0.2.50/24 dev eth0
A static route looks like it belongs in that list and does not.
VyOS hands static routes to FRR’s staticd, and it is zebra
that installs them into the kernel:
# VyOS: static route
set protocols static route 10.0.0.0/24 next-hop 192.0.2.1
# Generated FRR:
ip route 10.0.0.0/24 192.0.2.1
That is why a static route shows up in vtysh -c 'show ip route'
as well as in ip route show, and why a static route that is
missing from the kernel is a zebra question rather than an
iproute2 one.
The firewall is where the tidy one-line mapping stops. On the 1.4+ tree the rule set and the place it runs are two separate configurations:
# VyOS: the rule set itself
set firewall ipv4 name WAN-INBOUND rule 10 action accept
set firewall ipv4 name WAN-INBOUND rule 10 protocol tcp
set firewall ipv4 name WAN-INBOUND rule 10 destination port 22
# A named rule set on its own filters nothing. A base chain has
# to jump to it — here, traffic terminating on the router:
set firewall ipv4 input filter rule 10 action jump
set firewall ipv4 input filter rule 10 jump-target WAN-INBOUND
# Read the rendered ruleset back from nftables:
sudo nft list ruleset
One VyOS rule set becomes an nftables chain, and the jump rule
becomes a separate rule in a base chain that targets it. Two
tree branches, one runtime effect — and if you configure only
the first, commit succeeds, show configuration commands
looks right, and nothing is filtered. That is the most common
way a VyOS firewall silently does nothing.
The generated FRR config is not the same as the saved config
The VyOS saved configuration (/config/config.boot) is the
operator’s source of truth. The generated FRR config is
runtime state — what FRRouting has loaded right now. The two
may diverge if:
- The VyOS configuration was edited but not committed.
- FRR has been reloaded outside the VyOS CLI.
- A migration script updated one but not the other.
The operator must understand that the generated FRR config is not authoritative. The VyOS tree is authoritative; the generated FRR config is derived from it.
Reading the generated config for diagnosis
When a routing protocol misbehaves, the operator’s first action is to read the generated config. The pattern:
vtysh -c 'show running-config'
Look for:
- The configuration matches the VyOS tree. If not, there is a translation bug or a stale config.
- The configuration is what FRRouting has loaded. If not, there is a reload needed.
- The peer / neighbour / interface is configured correctly. If not, fix the VyOS tree.
If the generated config looks correct, the issue is in
FRRouting’s operational state — sessions are not
establishing, LSAs are not propagating, BGP updates are not
arriving. The operator then moves to show ip bgp summary,
show ip ospf neighbor, show bfd peer, and the diagnostic
commands for each protocol.
Failure modes
VyOS tree updated, FRR config not reloaded
The operator edits the VyOS tree and commits, but FRR is still running with the old config. The new tree is in memory; the old config is in FRR.
Causes:
- configd crashed mid-commit.
- or the FRR reload step of the commit failed silently.
Diagnostic:
vtysh -c 'show running-config'shows the old config.show configuration commands | match 'protocols bgp'shows the new config.- The two disagree.
Fix:
- Force VyOS to re-render the branch.
commitapplies deltas only, so committing again with nothing changed does nothing at all.deletethe stanza,commit,setit again,commit— now there is a delta for configd to act on. - If that does not take,
sudo systemctl restart frr. This is a blunt instrument: it drops every routing adjacency on the box and reconverges from scratch, so it is a maintenance-window action on a router carrying traffic. - Do not hand-apply the missing stanza in
vtysh. It papers over the divergence and the next commit removes it.
FRR config edited outside the VyOS CLI
The operator runs vtysh directly and edits the config. The
VyOS tree does not know about the change.
Causes:
- Operator used vtysh for a one-off fix.
- A script or automation tool ran
vtyshoutside the VyOS CLI.
Diagnostic:
vtysh -c 'show running-config'shows the change.show configuration commandsshows the VyOS tree, and does not.- The two disagree.
Fix:
- Re-apply the change via the VyOS CLI.
- Or accept the divergence and document it.
Operational commands
The operator reads the generated state with:
# Show the generated FRR running-config
vtysh -c 'show running-config'
# Narrow it to one daemon
vtysh -d bgpd -c 'show running-config'
# Show the VyOS tree for one branch, in set-command form
show configuration commands | match 'protocols bgp'
# Show the kernel state the two of them produced
ip route show
sudo nft list ruleset
There is no built-in comparison between the VyOS tree and the
FRR running-config, and no useful diff between them either:
they are two different syntaxes describing the same intent, so
a textual diff is noise. The comparison is a reading exercise.
Take one branch at a time — this neighbour, this prefix list,
this route map — and check that what FRR loaded says what the
tree asked for.
Validation
The validation sequence for “the generated config is wrong”:
- The VyOS tree:
show configuration commandsshows the operator’s intent. - The FRR running-config:
vtysh -c 'show running-config'shows what FRR has loaded. - The kernel state:
ip route show,ip neigh show,sudo nft list rulesetshow what is actually running. - The diff between VyOS and FRR: manual diff shows whether the translation is correct.
- The diff between FRR and kernel: zebra-vs-kernel diff shows whether zebra is correctly applying.
If the VyOS tree and FRR config disagree, the translation broke. If the FRR config and kernel state disagree, zebra broke.
Cross-course references
- The Linux course’s
XX-Linux-NetConfigcovers the underlying kernel state the generated config translates to. - The OPNsense course covers the equivalent FreeBSD config-to-config translation.
- The lesson on FRRouting and the routing daemons in this part covers the daemons that consume the generated config.
Quiz
Knowledge check · 4 questions
Q1. You commit a new BGP neighbour. `vtysh -c 'show bgp summary'` does not show the new neighbour. What is the most likely cause?
The operator runs: `set protocols bgp neighbor 198.51.100.1 remote-as 65002` `commit` The local AS was already set with `set protocols bgp system-as 65000`. `show configuration commands | match 'protocols bgp'` shows the new neighbour. `vtysh -c 'show running-config'` does not include it. `vtysh -c 'show bgp summary'` does not show the new neighbour.
Q2. What does `set interfaces ethernet eth0 address 192.0.2.50/24` translate into at the kernel?
Q3. The VyOS configuration tree is the source of truth for what the router is configured to do, and the generated FRR config is derived from it.
Q4. You edit FRR config via vtysh to add a temporary BGP neighbour. The next commit overwrites your change. What went wrong?
The operator runs `vtysh`, configures a one-off BGP neighbour, saves FRR config with `write memory`. The neighbour works. The next time the operator commits a VyOS change, the VyOS tree's bgp section overwrites FRR's running-config, removing the one-off neighbour.
Passing score: 75%. Answers are checked in this browser.
Production discipline
The generated runtime configuration is the bridge between the VyOS tree and the kernel state. The operator who understands the bridge can diagnose “the VyOS config is right but the kernel is wrong” incidents in minutes instead of hours.
Plan the translation rules once. Document the divergence patterns. Use the VyOS CLI for everything. Then either the translation is correct or the operator finds the divergence quickly.