Wheelhouse docs

Uplinks (multi-WAN)#

The first tab of High availability, at /ha. It is the one place to see and change every way this router can use more than one internet connection. It edits nothing itself: every action stages the commands an operator would type, and the Commit Bar applies them.

VyOS has no gateway object. An uplink is any interface that carries a default route, and there are five ways an interface can get one. This page finds all five, ranks them, joins each with the live forwarding table, and tells you which one is carrying traffic right now.

The five mechanisms#

MechanismConfigurationRanking knobDefault
Staticprotocols static route 0.0.0.0/0 next-hop <gw> distance <n>distance1
DHCP clientinterfaces … address dhcpdhcp-options default-route-distance210
PPPoE clientinterfaces pppoe <name>default-route-distance1
Failoverprotocols failover route 0.0.0.0/0 next-hop <gw> or … dhcp-interface <if>metric1
Load balancingload-balancing wan interface-health <if>rule weights1

An interface can be an uplink through several of these at once — that is normal, and the Why it is an uplink line in the side panel lists every source that made it one: static-route, dhcp, pppoe, failover, load-balancing, policy-table.

Where the data comes from#

GET /api/wan, polled every 10 seconds, is one model assembled from six reads — agent/wan.go (handleWAN, buildWANView):

ReadUsed for
config interfacesaddressing, descriptions, DHCP and PPPoE options; VLAN vif children are flattened, so eth0.10 can be an uplink
config protocolsstatic default routes and the whole failover subtree
config load-balancingmembers, health tests and rules
config policypolicy-route rules and the tables they select
config natwhich source-NAT rule masquerades behind each uplink
show ip routethe live standing of every default route
show interfaces <kind> <name>, per interfacelink state and live addresses
show wan-load-balancethe balancer's own verdict — only when it is configured, because the op-mode script refuses otherwise and a refusal is not a health report

If show ip route fails, the response carries routes_error, every health verdict becomes unknown, and a warning says the forwarding table could not be read.

The four tiles#

TileValueSub-line
Uplinkshow many were foundthe mode in force
Carrying trafficthe interface holding the selected default routeits gateway, or no selected default route
Healthpassing over monitoredchecked uplinks passing, or no health checks configured
Policy routinghow many policy rules existhow many alternate tables

The mode is computed from what is configured, not from what is working:

ModeShown asWhen
noneno uplinkno interface carries a default route
singlesingle uplinkexactly one does
distanceranked by distance, no checksmore than one, none checked or balanced
failoverhealth-checked failoverat least one protocols failover entry
load-balancingload-balancedload-balancing wan configured, no failover entry
mixedfailover + load-balancingboth
ColumnWhat it shows
Uplinkthe interface name, with its description underneath
Addressstatic, dhcp, pppoe or unknown, plus the live addresses (falling back to the configured ones)
Gatewaydeclared for a static route, learned from the forwarding table for a DHCP client
Rankthe role badge and the number, with the knob that sets it as the hover title
Default routeselected, installed, present or absent
Healthpassing, failing, link down, no check or unknown, with the check targets beside it
Source NATthe rule number and translation, by the load balancer, or a missing badge
(last)a count of notes, when the uplink has any

Role, and how it is decided#

Uplinks that are members of a non-exclude load-balancing rule are balanced. Everything else with a rank is sorted: uplinks that have a failover check come first, ordered by their metric; then the rest, ordered by distance. The first is primary, the others backup. An uplink with no distance source at all is unranked.

Checked uplinks sort ahead of unchecked ones for a concrete reason. An installed failover route is a kernel route at distance 0, so it beats every static and DHCP default route regardless of what their distances say. Among checked uplinks, the failover metric is the rank.

Default route, and the four states#

BadgeMeaning
selectedthis route is > and * in the forwarding table: traffic is leaving here
installedit is * but not >: the kernel has it, another route wins
presentFRR knows the route and it is not in the forwarding table
absentthere is no default route through this interface at all

Where an interface has several default routes, the page shows the one with the best standing — selected beats installed beats present.

Health, and where each verdict comes from#

Health is the router's own opinion. The agent probes nothing.

VerdictSource
link downthe kernel reports the interface DOWN. Checked first, before anything else
passing / failing (checked uplink)whether the failover daemon's kernel route for this uplink is in the forwarding table right now
passing / failing (balanced uplink)Status: active or failed in show wan-load-balance
unknownthe forwarding table could not be read, or the balancer has not reported
no checkthe uplink has neither a failover check nor an interface-health block

What the router says#

When the model produces any router-wide warnings, a panel above the table lists them. These are the ones it can raise:

MessageWhat it means
… share distance N: that is equal-cost multipath, not failovertwo unchecked uplinks have the same distance. The kernel will use both at once for new flows, which is not what "backup" means. Give the backup a higher number, or add checks
<iface> has no health check; its route is never withdrawn when the path breaksraised for every uplink without one, once any uplink has one. A provider that is up at layer 2 and dead beyond it keeps carrying traffic
static default route via <gw> matches no interface address; it cannot be attributed to an uplinkthe gateway is not on any connected subnet and is not in the forwarding table, so the page cannot say which interface it belongs to
failover route via <value> names no interface and matches nonethe same problem on a protocols failover entry. Add interface <if> to it
the forwarding table could not be read (…); health is unknownshow ip route failed
WAN load-balancing is configured but the daemon has not reported (…)show wan-load-balance failed. Usually the daemon is not running

Per-uplink warnings appear in the side panel and are counted in the table's last column:

MessageWhat it means
link is downthe kernel says so
no source NAT rule leaves through <iface>; traffic sent this way keeps its private addressesraised when there is more than one uplink and the balancer is not writing NAT rules
failover route has no check target; VyOS will refuse to commit ita protocols failover entry with no check target is invalid
dhcp-options no-default-route is set: on this platform the failover daemon then installs no route for a dhcp-interface entrysee below

And the notes, which are facts rather than faults:

  • the static default route via <gw> stays as the last resort: outranked by any installed failover route, used only when every checked route is withdrawn.
  • the <dhcp|pppoe> client's own default route (distance N) stays as the last resort: same reasoning.

Both exist because the obvious tidy-up — deleting the client's own route once a failover check is in place — is the thing that breaks it.

Clicking a row opens it. Live at the top: link, addresses, gateway, the default route's standing in words, the route's protocol and distance as FRR reported them, health, and Why it is an uplink. Warnings then notes below that.

Distance#

One field, pre-filled with the current rank. What it writes depends on how the uplink gets its address:

set interfaces ethernet eth1 dhcp-options default-route-distance 220
set interfaces pppoe pppoe0 default-route-distance 20
set protocols static route 0.0.0.0/0 next-hop 203.0.113.1 distance 20

The field is disabled when the uplink has no distance source, and when its rank comes from a failover metric — because then the metric is the rank, and the metric is edited in the health-check editor below.

For a VLAN uplink the interface path is rebuilt as interfaces <kind> <parent> vif <id>, so eth0.10 writes to the right node.

Health check#

+ Add, or Edit when one exists, opens the failover editor. The node it writes is protocols failover route 0.0.0.0/0 followed by either next-hop <gateway> — for a static uplink — or dhcp-interface <interface> for a DHCP one. An uplink with neither a gateway nor DHCP addressing cannot have a check, and the editor says so: This uplink has no gateway to check through. Give it a static default route first.

FieldCommand writtenDefault it omits
Check targets… check target <addr>, one per target
Type… check type tcp or … check type arpicmp is the default and is not written
Port… check port <n>written only for tcp, where it is required
Every (seconds)… check timeout <n>10 is not written; the hint is 1–300
Metric… metric <n>
Verdict… check policy all-availableany-available is not written

For a next-hop entry the editor also writes … next-hop <gw> interface <iface>, which is what lets the agent attribute the entry to an interface later.

A worked result:

set protocols failover route 0.0.0.0/0 dhcp-interface eth1 check target 192.0.2.10
set protocols failover route 0.0.0.0/0 dhcp-interface eth1 check target 198.51.100.10
set protocols failover route 0.0.0.0/0 dhcp-interface eth1 check timeout 5
set protocols failover route 0.0.0.0/0 dhcp-interface eth1 metric 1

Verdict is the difference between any target answers (the default) and all targets answer. Two targets with any-available survives one target going away; all-available treats one unreachable target as a dead uplink, which is stricter than most people want from a public address.

Remove stages delete protocols failover route 0.0.0.0/0 <key> <value> and closes the panel.

Source NAT#

When nothing masquerades behind this uplink — and the load balancer is not writing NAT rules itself — the panel says so, previews three commands and offers a button:

set nat source rule 100 outbound-interface name eth1
set nat source rule 100 translation address masquerade
set nat source rule 100 description 'Wheelhouse: masquerade behind eth1'

The rule number is the next free one: 100 when no source rule is numbered 100 or above, otherwise the highest plus 10. When a rule does exist, the panel names it and links to the NAT page, where its match conditions can be edited — see Source NAT.

The red footer button stages everything that makes this interface an uplink, in one set, and leaves the interface itself alone:

delete protocols static route 0.0.0.0/0 next-hop 203.0.113.1
set interfaces ethernet eth1 dhcp-options no-default-route
delete protocols failover route 0.0.0.0/0 dhcp-interface eth1
delete load-balancing wan interface-health eth1

Only the lines that apply are staged. Note that this is the one place the product writes no-default-route on a DHCP interface — and it does so in the same set that removes the failover entry, so the combination the warning above is about is never created.

It does not remove the source-NAT rule. That stays until you delete it on the NAT page.

Failover health checks#

The panel below the table lists every uplink that has a protocols failover entry, with its metric, its check, the interval and verdict policy, and a badge reading route installed or route withdrawn. Remove check on a row stages the delete.

With no entries it says so in one line: No uplink has a health check. Open an uplink and add one.

How the mechanism behaves, in one paragraph: the failover daemon installs a default route for an uplink while its check targets answer through that uplink, and withdraws it when they stop. Among the installed ones the lowest metric wins. The route it installs is a kernel route at distance 0, so while it is there it beats every static and client-installed default route; those are the last resort, used only when every checked route has been withdrawn.

Verified on a dual-WAN VM on 2026-09-02: the daemon adds ip route … proto failover, FRR lists it as K>* 0.0.0.0/0 [0/<metric>], and cutting a link turns that uplink to link down / route withdrawn within the check interval while the other keeps the selected route — docs/multi-wan.md.

Load balancing#

load-balancing wan spreads new connections across member uplinks by weight and drops a member whose interface-health tests fail. Its verdicts come from show wan-load-balance.

The five options#

Each is a checkbox that stages a set or a delete immediately — there is no editor around them.

CheckboxPathWhat it does
Sticky inbound connectionsload-balancing wan sticky-connections inboundreplies leave through the uplink the connection arrived on
Do not write source NATload-balancing wan disable-source-natkeep your own NAT rules; the balancer masquerades per uplink otherwise
Balance router-originated trafficload-balancing wan enable-local-trafficoff by default: only forwarded traffic is balanced
Flush conntrack on state changeload-balancing wan flush-connectionsexisting flows move immediately when an uplink fails or returns
Prefer specific routesload-balancing wan only-default-routeonly traffic that would use the default route is balanced

Whether the balancer writes NAT is what the uplinks table's Source NAT column reads to decide between by the load balancer and a missing badge.

Interface health#

One line per balanced uplink: its nexthop, its tests, fail after N · recover after N (from failure-count and success-count, both defaulting to 1), the daemon's status badge with its failure count, and when the status last changed. An uplink with no report shows no report rather than a green badge.

Rules#

ColumnMeaning
Rulethe rule number
Frominbound-interface
Matchprotocol, source address and port, destination address and port — or any
Uplinkseach member with its weight, or excluded from balancing
flagsfailover and per-packet badges

Delete stages delete load-balancing wan rule <n>.

A configuration with health tests and no rules balances nothing, and the empty table says exactly that: Health tests exist but no rule uses them; nothing is balanced.

Adding a rule#

+ Add balancing rule builds one rule and, for any member that does not have an interface-health block yet, the health test that member needs — because the balancer refuses a member without one.

set load-balancing wan rule 10 inbound-interface eth0
set load-balancing wan rule 10 interface eth1 weight 3
set load-balancing wan interface-health eth1 nexthop dhcp
set load-balancing wan interface-health eth1 test 10 target 192.0.2.10
set load-balancing wan rule 10 interface eth2 weight 1
set load-balancing wan rule 10 failover
set load-balancing wan rule 10 description 'general traffic'

The nexthop it writes is the literal dhcp for a DHCP uplink and the gateway address otherwise. The test target defaults to the uplink's gateway, and the field's tooltip says what that is worth: the gateway is a weak one. A gateway that answers proves the first hop is alive and nothing beyond it. Point the test at something on the far side of the provider.

The other controls: Exclude matching traffic from balancing writes … rule <n> exclude and nothing else, which keeps matching traffic on the main routing table; Failover writes … failover, moving flows off a member whose tests fail; Balance per packet rather than per connection writes … per-packet-balancing, and its hint is the warning — it breaks anything that expects a stable source address, which is most things.

Remove load balancing in the panel header stages delete load-balancing wan, the whole subtree.

Policy routing#

"Send the cameras out through the LTE modem" is not a ranking question, so it is not distance. It is a policy route rule that marks matching packets for an alternate routing table whose only route is that uplink's gateway.

The panel lists the alternate tables — protocols static table <n> — with their routes and the uplink each resolves to, and then each policy route <name> with the interfaces it is bound to and its rules.

+ Route traffic via an uplink builds both halves. It creates the table only if no existing table already resolves to the chosen uplink:

set protocols static table 10 route 0.0.0.0/0 dhcp-interface eth2
set policy route WAN-POLICY rule 10 source address 10.0.5.0/24
set policy route WAN-POLICY rule 10 set table 10
set policy route WAN-POLICY interface eth0

For a static uplink the table route is next-hop <gateway> instead. An uplink that is neither DHCP-addressed nor has a known gateway produces no commands at all.

Table numbers are suggested in tens from 10 to 200, falling back to any free number in 1–200; rule numbers are the highest existing plus 10, or 10. The Applies to traffic entering on list is the router's LAN interfaces — every configured interface that has an address, is not an uplink, and is not a WireGuard, tunnel or VTI interface. When there are none, the panel says so: No LAN interface with an address; the policy would bind to nothing.

The rule needs at least one match — a source, a destination, a protocol or a destination port — or nothing is staged.

+ Add uplink takes an interface that is not already an uplink and stages, in one go: the addressing, a distance that keeps it behind the current uplinks, an optional failover check, and a masquerade rule.

The Interface menu lists candidates: every configured interface that is not already an uplink, excluding loopbacks and dummies. Its hint shows the addresses that interface currently holds.

DHCP#

set interfaces ethernet eth2 address dhcp
set interfaces ethernet eth2 dhcp-options default-route-distance 220

Static#

Both the address and the gateway are required.

set interfaces ethernet eth2 address 203.0.113.2/24
set protocols static route 0.0.0.0/0 next-hop 203.0.113.1 distance 20

PPPoE#

The username and password are required. The PPPoE interface is created over the physical one; its default name is pppoe0.

set interfaces pppoe pppoe0 source-interface eth2
set interfaces pppoe pppoe0 authentication username <user>
set interfaces pppoe pppoe0 authentication password <secret>
set interfaces pppoe pppoe0 default-route-distance 20

The password field's hint is a plain statement of fact: it is stored in the router's configuration, which is where PPPoE credentials have to live. It is a password leaf, so GET /api/config blanks it for anyone below admin — agent/security.go.

The rest of the panel#

Distance is pre-filled with a suggestion: the highest existing distance plus 10, or 210 for a first DHCP uplink and 1 otherwise. Description is optional and is written to whichever interface node was created.

Masquerade behind it is ticked by default and adds two commands using the next free source rule number:

set nat source rule 110 outbound-interface name eth2
set nat source rule 110 translation address masquerade

Withdraw its route when a check target stops answering adds a protocols failover entry with the targets you list, ICMP type, a 10-second interval, any-available, and a metric equal to the distance capped at 255.

Everything is previewed as commands before it is staged.

What this page will not do#

  • It probes nothing. Every health verdict is the router's own.
  • It does not reorder or renumber existing rules.
  • It does not touch the firewall. A second WAN interface needs the same input policy as the first. That is the Firewall page's job, and forgetting it is how a backup uplink ends up being the open one.
  • It has no IPv6 concept. The whole model looks for 0.0.0.0/0 and nothing else.
  • It shows no per-target latency or loss, and no history of transitions. See the platform gap above.

eth1 is the existing DHCP uplink. eth2 is cabled to a second provider.

  1. Network → High availability → + Add uplink.
  2. Interface eth2; Address DHCP from the provider.
  3. Leave the suggested distance. Description ISP B.
  4. Leave Masquerade behind it ticked.
  5. Tick Withdraw its route when a check target stops answering and give it two targets on the far side of the provider.
  6. Read the Commands block. It should contain the address, the distance, the two NAT lines, and four or five protocols failover lines.
  7. Stage uplink, then Commit-confirm, then confirm.

Now give the first uplink a check as well — a standby with a check beside a primary without one is the worst of both, because the primary's route is never withdrawn:

  1. Click the eth1 row, Health check → + Add, the same targets, metric 1.
  2. Set eth2's metric to 2 from its own panel.
  3. Stage and commit.

The table should now show eth1 primary, health passing, default route selected; eth2 backup, health passing, default route installed. Both should have a source-NAT rule. The Health tile should read 2/2 and the mode should be health-checked failover.

To test it, take eth1's link down and watch: within the check interval eth1 should turn to link down / route withdrawn and eth2's route should become selected. That is the sequence the dual-WAN VM verification runs, on 2026-09-02 — docs/multi-wan.md.

See also#

  • High availability — the page these tabs belong to.
  • Addressingdhcp, no-default-route and the client's own default route.
  • Static routes — the forwarding table this page reads, and where the kernel route at distance 0 comes from.
  • VRRP — the other half of high availability: more than one router.
  • Interfaces — link state, counters and MTU for the uplink interfaces.
  • Source NAT — the masquerade rules this page offers to write, and everything else that can be done to them.

Checked against agent/wan.go, ui/src/pages/Wan.tsx, ui/src/pages/Ha.tsx, docs/multi-wan.md, agent/parse.go (parseRoutes), agent/security.go, ui/src/lib/format.ts.

Updated 2026-09-02 manual network multi-wan failover load-balancing policy-routing nat