VyOSXXXVI · ECMPECMP
BGP ECMP — maximum-paths, eBGP/iBGP, multipath-relax, AS_PATH length requirement
What you'll learn
- Configure `maximum-paths` for eBGP and iBGP separately
- Apply `multipath-relax` for AS_PATH-unequal peers
- Diagnose why only one path is selected when ECMP is expected
- Trace the BGP best-path tie-break that prevents multipath
- Roll back a BGP ECMP change safely with `commit-confirm`
Prerequisites
- AS path — prepend, aggregation loss, and the loop prevention
- IGP cost tiebreak — when AS Path and other attributes tie, lowest IGP cost to next-hop wins
- ECMP concept — Equal-Cost Multi-Path, kernel flow-based or route-based hashing, throughput scaling
- ECMP configuration — multiple next-hops, max-paths, load-balancing algorithm
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)
BGP ECMP requires the paths to be “equal” by the best-path algorithm. The best-path algorithm has 11 tie-breakers, and ECMP requires the paths to be equal up to and including the IGP cost tie-break. If any earlier tie-break separates the paths (different weight, local-preference, AS-path length, origin, MED, or eBGP/iBGP), only one path is installed.
This lesson is the operator’s reference for BGP ECMP: the
maximum-paths configuration, the multipath-relax knob, the
per-peer next-hop tracking, and the production failure modes
that arise when the best-path algorithm picks one path
despite the operator expecting ECMP.
The BGP best-path algorithm in one minute
FRR 10.x runs its route selection as an ordered list, and the multipath decision sits inside that list rather than beside it:
- Lowest administrative distance.
- Highest
weight. - Highest
local-preference. - Locally originated (network statement, aggregate, redistribute).
- Shortest
AS_PATHlength. - Lowest
origin(IGP < EGP < incomplete). - Lowest
MED— compared only between paths from the same neighbouring AS, unlessalways-compare-medis set. - eBGP preferred over other peer types.
- Lowest IGP cost to the next-hop.
- Multipath check — the paths that have tied through step 9 become the multipath set.
- Already-selected external route (stability).
- Lowest router-ID.
- Shortest cluster-list.
- Peer address, as a last resort.
Read step 10 carefully, because it is the whole lesson. Paths become an ECMP set by surviving the earlier steps together. Anything that separates two paths at step 5, 7 or 9 has already eliminated one of them before multipath is considered. ECMP is not a mode you turn on; it is what happens when nothing broke the tie.
maximum-paths configuration
On VyOS 1.5 maximum-paths lives under the address family, and
it always carries a peer-type qualifier:
configure
set protocols bgp system-as 64512
set protocols bgp address-family ipv4-unicast maximum-paths ebgp 2
set protocols bgp address-family ipv4-unicast maximum-paths ibgp 4
commit
save
Two things to be precise about, because the 1.3-era shape of this command encouraged a wrong mental model:
- There is no unqualified
maximum-paths. The node ismaximum-paths <ebgp|ibgp> <1-256>; the qualifier is mandatory. A bareset protocols bgp maximum-paths 4is not a command on this tree, and it never meant “eBGP and iBGP combined” even where it parsed. - It is per address family. IPv4 and IPv6 unicast each carry their own cap. Setting it for
ipv4-unicastand expecting dual-stack ECMP is a common and completely silent omission — the IPv6 side simply keeps installing one path.
The defaults are 1 for both, so ECMP is opt-in. The example above reads as: two eBGP peers, four iBGP paths. Size the numbers to the topology rather than setting them high “just in case” — the cap is also the ceiling on how much churn a single prefix’s next-hop set can produce in the FIB.
multipath-relax — same length, different ASes
This knob is routinely described wrongly, including in a lot of vendor-adjacent blog writing, so it is worth quoting FRR’s own definition:
This command specifies that BGP decision process should consider paths of equal AS_PATH length candidates for multipath computation. Without the knob, the entire AS_PATH must match for multipath computation.
Read that twice. The default is not “equal length”; the default
is identical AS_PATH. Two paths of length 2 reading
64513 64515 and 64514 64515 are not multipath candidates
by default, even though they tie at every best-path step,
because the paths are not the same sequence.
multipath-relax loosens that comparison from “identical” to
“same length”:
configure
set protocols bgp parameters bestpath as-path multipath-relax
commit
save
flowchart TB
subgraph EQ["Equal length, different ASes — multipath-relax applies"]
A1["path A: 64513 64515"]
A2["path B: 64514 64515"]
A3["default: not multipath (paths differ)"]
A4["with multipath-relax: both installed"]
end
subgraph NE["Different lengths — multipath-relax does NOT apply"]
B1["path A: 64513"]
B2["path B: 64514 64516 64517"]
B3["step 5 eliminates B before the multipath check"]
B4["with multipath-relax: still only A"]
end
The eBGP/iBGP separation
Step 8 prefers eBGP over other peer types, so an eBGP path and
an iBGP path for the same prefix are separated before the
multipath check and only the eBGP one is installed. Raising
maximum-paths ebgp or maximum-paths ibgp does not change
this: those are caps applied within a peer type, not a way to
merge across types.
The knob that does merge them is a bestpath parameter of its own:
set protocols bgp parameters bestpath peer-type multipath-relax
FRR describes it as making the decision process “consider paths from all peers for multipath computation… paths learned from any of eBGP, iBGP, or confederation neighbors will be multipath if they are otherwise considered equal cost.”
Note the qualifier at the end. It relaxes step 8 only. Every
other step still applies, so an iBGP path with a different
AS_PATH still needs as-path multipath-relax as well, and an
iBGP path whose next-hop has a higher IGP cost is still
eliminated at step 9.
MED and the always-compare-med knob
MED (Multi-Exit Discriminator) is step 7 of the selection
algorithm, and by default it is compared only between paths
from the same neighbouring AS. Two transits in different ASes
therefore never have their MEDs compared at all — which means
a MED difference between them cannot be the reason your ECMP
is not forming, and always-compare-med cannot be the fix.
On VyOS 1.5 the knob sits directly under parameters, not
under bestpath med:
configure
set protocols bgp parameters bestpath as-path multipath-relax
set protocols bgp parameters always-compare-med
commit
save
The bestpath med node exists but holds different options —
confed and missing-as-worst. always-compare-med is its
own leaf one level up. This is the kind of near-miss that
autocompletes into the wrong place if you are typing from
memory of another vendor’s CLI.
IGP cost tie-break and ECMP
Step 9 is “lowest IGP cost to the next-hop”, and it is the last step before the multipath check. For an ECMP set to form, the IGP costs to every candidate next-hop must be equal.
Two eBGP peers, ISP-A at 192.0.2.1 and ISP-B at 192.0.2.2, with
IGP cost 5 to the first and 10 to the second: BGP selects ISP-A
and never reaches the multipath check with ISP-B still in play.
This is the failure that survives both knobs, because neither
maximum-paths nor multipath-relax touches step 9 — which is
why an operator who has configured both and still sees one path
should look here next.
The fix is to align the costs on the links to each peer:
configure
set protocols ospf interface eth0 cost 5
set protocols ospf interface eth1 cost 5
commit
save
Two cautions before you do that. The OSPF interface cost is not private to this decision — every SPF calculation in the area uses it, so equalising two links to make BGP multipath form also changes how transit traffic moves through the router. And if these are directly connected eBGP peers, they may not be in OSPF at all; the “IGP cost” FRR compares is then the cost of whatever route resolves the next-hop, most often a connected route at cost 0 for both, in which case they already tie and step 9 is not your problem.
Check before adjusting:
show ip route 192.0.2.1
show ip route 192.0.2.2
Compare the metric on the two resolving routes. If they already match, the separation is happening at an earlier step and raising or lowering an interface cost will change traffic patterns without fixing anything.
Validation
# 1. The candidate paths exist in the BGP table, and which of
# them BGP marked as multipath
show bgp ipv4 10.0.0.0/24
# 2. The knobs are actually in the configuration
show configuration commands | match maximum-paths
show configuration commands | match multipath-relax
# 3. The kernel has both next-hops. This is the one that
# matters: a prefix can be multipath in BGP and single-path
# in the FIB if something below BGP dropped it
show ip route 10.0.0.0/24
# 4. The IGP cost to each next-hop, for comparison
show ip route 192.0.2.1
show ip route 192.0.2.2
Step 1 is the important reading and it is worth being specific about what to look for rather than trusting a flag. Compare the two paths attribute by attribute in that output: local-pref, AS_PATH, origin, MED, and whether each is marked internal. The first attribute that differs is the step that eliminated the second path, and it tells you which of the fixes in this lesson is the relevant one — or that none of them is.
A clean result is three agreeing observations: BGP shows more
than one path contributing, show ip route shows more than one
next-hop for the prefix, and the resolving routes for those
next-hops have equal metrics. If BGP shows multipath and the
kernel shows one next-hop, the problem is below BGP — usually a
next-hop that does not resolve — and no amount of BGP tuning
will move it.
Hashing, and why “the traffic did not split evenly” is often not a bug
ECMP in the Linux kernel is per-flow, not per-packet: a hash over the packet headers picks the next-hop, and every packet of a flow hashes the same way so TCP does not reorder. By default the IPv4 hash covers layer 3 only. VyOS exposes the layer-4 variant:
set system ip multipath layer4-hashing
With layer-3 hashing, a single source talking to a single destination lands entirely on one next-hop no matter how much traffic it sends — one flow, one hash, one path. Adding the ports to the hash lets separate connections between the same pair of hosts spread across the paths.
This is why a two-path ECMP set tested with one large transfer looks broken and is not. Test with many distinct flows, and judge the result by the interface counters over minutes rather than by a single measurement.
Production failure modes
Only one path despite the knobs being set
The single most common report, and it has one diagnostic:
show bgp ipv4 10.0.0.0/24
Put the two paths side by side and walk the algorithm in order — local-pref, locally originated, AS_PATH, origin, MED, peer type, IGP cost. The first attribute that differs is the answer, and everything after it is irrelevant. Working from the list in order is what turns this from guessing into a two-minute check.
Mapped to the fix:
| First difference | Step | Fix |
|---|---|---|
| local-preference | 3 | Align it. This is policy you wrote; find the route-map. |
| AS_PATH length | 5 | Not fixable with multipath-relax. Prepend on the shorter side, or accept the asymmetry. |
| AS_PATH content, same length | 5 | bestpath as-path multipath-relax. This is the case the knob exists for. |
| origin | 6 | Usually a redistribution somewhere producing incomplete. Fix it at the source. |
| MED, same neighbour AS | 7 | Align the MEDs, or ask the neighbour to. |
| MED, different neighbour AS | 7 | Not being compared at all. If this looks like the difference, it is not — look further down. |
| internal vs external | 8 | bestpath peer-type multipath-relax, and read the design caveat above first. |
| IGP cost | 9 | Align the resolving routes’ metrics. |
| Nothing differs | 10 | The paths are tied; the cap is the limit. Check maximum-paths is set for this address family. |
That last row is worth its own sentence. maximum-paths
defaults to 1, and its absence looks exactly like a tie-break
problem: two identical paths, one installed. Check the
configuration before you go looking for an attribute
difference that is not there.
multipath-relax configured, and it changed nothing
The operator has two transits, sees one path, sets
bestpath as-path multipath-relax, commits, and nothing moves.
Diagnostic: compare the AS_PATH lengths, not just their contents. If they differ, this knob was never going to help: length is decided at step 5 and the longer path is eliminated before the multipath check. The knob only relaxes the requirement that equal-length paths be identical.
Fix: prepend on the shorter path’s inbound policy to equalise the lengths, or treat the shorter path as primary and the other as backup — which, for two transits of genuinely different topological distance, is usually the correct outcome anyway.
An iBGP path is never in the set with an eBGP path
Two paths for the same prefix, one learned over eBGP and one over iBGP, and only the eBGP one is installed.
Diagnostic: show bgp ipv4 10.0.0.0/24 marks the iBGP path
internal. Step 8 prefers eBGP, so it is eliminated before the
multipath check.
Fix: this is the default and usually the right behaviour —
prefer your own exit over hairpinning through another router.
If you genuinely want both,
set protocols bgp parameters bestpath peer-type multipath-relax
is the knob, and the internal path needs enough capacity to
carry its share.
The set forms and then churns
The ECMP set forms, but one member’s session or next-hop is unstable. Every appearance and disappearance changes the size of the next-hop set, which changes the hash, which moves flows that were not on the failing path at all. Users on the healthy path see resets.
Diagnostic:
show bgp ipv4 summary— the Up/Down column on each contributing peer. A member whose uptime keeps resetting is the source.journalctl -u frrfor the session’s reset reasons.
Fix: stabilise the member before tuning anything else. Failing that, remove the unstable path from the set deliberately rather than letting it join and leave — a stable two-path set beats a three-path set that is really two paths plus a metronome. BFD shortens the detection window on a genuinely failing path; it does nothing for one that is flapping between working states.
Rollback
Capture the configuration before the change, from configuration mode, so there is a file to diff against afterwards:
configure
save /config/pre-ecmp-change.boot
Then make the change under commit-confirm, so a mistake
reverts itself rather than waiting for you to notice:
commit-confirm 5
If it looks right, confirm within the window; if it does not,
do nothing and the router restores the previous configuration
on its own.
To undo a committed ECMP change, delete the specific nodes:
configure
delete protocols bgp address-family ipv4-unicast maximum-paths ebgp
delete protocols bgp parameters bestpath as-path multipath-relax
commit
save
Traffic collapses back onto the single best path immediately — this is a data-plane change taking effect at commit, not a gradual reconvergence — so the remaining link carries the whole load from that moment. Size that before rolling back during business hours.
Note that rollback <N> is a different mechanism: it reverts
the entire configuration to a stored revision and currently
requires a reboot on VyOS. For undoing two BGP nodes, deleting
them is faster and does not take the router down.
Production discipline
Cross-course references
- Part XXVI-02 (
XXVI-VyOS-BGPAttributes/ AS-path) covers the AS-path attribute and the tie-break. - Part XXVII-05 (
XXVII-VyOS-BGPBestPath/ IGP cost tiebreak) covers the best-path algorithm in detail. - Part XXXVI-01 (
XXXVI-VyOS-ECMP/ concept) covers the conceptual model. - Part XXXVI-02 (
XXXVI-VyOS-ECMP/ config) covers the configuration of ECMP includingmaximum-paths. - Part XXXII (
XXXII-VyOS-BFD) covers BFD for fast failure detection.
Quiz
Knowledge check · 4 questions
Q1. R1 has two eBGP peers advertising 10.0.0.0/24 with AS_PATH lengths 1 and 3. `maximum-paths ebgp 4` is configured but only one path is installed. Which statement is correct?
Q2. On VyOS 1.5, an unqualified `set protocols bgp address-family ipv4-unicast maximum-paths 4` configures a single multipath budget shared between eBGP and iBGP paths.
Q3. R1 has two eBGP peers, ISP-A and ISP-B, both advertising 10.0.0.0/24 with equal-length AS_PATHs. `maximum-paths ebgp 4` and `bestpath as-path multipath-relax` are both configured, and the routing table still shows one next-hop. Walk the diagnosis and name the fix, including what the fix costs elsewhere.
R1 reaches ISP-A via next-hop 192.0.2.1 and ISP-B via 192.0.2.2. Both next-hops are resolved through OSPF, and the route to 192.0.2.1 has metric 5 while the route to 192.0.2.2 has metric 10. Both peers advertise 10.0.0.0/24 with AS_PATH length 2. `show bgp ipv4 10.0.0.0/24` lists both paths; `show ip route 10.0.0.0/24` shows only 192.0.2.1.
Q4. R1 has an eBGP transit session and an iBGP path via R2 for the same prefix. An engineer proposes `set protocols bgp address-family ipv4-unicast maximum-paths 8` to load-share across both. Explain why that command does not do what they think, what the correct knob is, and why you might still decline to use it.
R1 in AS 64512 peers with ISP-A (AS 64513) over eBGP and with R2 (AS 64512) over iBGP. Both offer 10.0.0.0/24 with the same AS_PATH; R2 learned it from its own transit session. R1 installs only the eBGP path. R1 and R2 are connected by a single 1 Gbit/s link; each of their transit circuits is 10 Gbit/s.
Passing score: 75%. Answers are checked in this browser.