Skip to main content
RunBook Academy

VyOSXXX · BGP Route ReflectorsRoute reflectors

Cluster-id — the deduplication key, and why it decides how your clients must peer

Advanced⏱ ~22 minshow ip bgpshow ip bgp summaryshow ip bgp neighborsshow configuration commandsvtysh

What you'll learn

  • Configure `set protocols bgp parameters cluster-id` on VyOS 1.5 LTS
  • State when a route reflector stamps ORIGINATOR_ID and CLUSTER_LIST, and when it does not
  • Explain why two RRs sharing a cluster-id must each peer with every client in the cluster
  • Predict which copy of a reflected route a client installs, using the real tie-break order
  • Diagnose a mismatched cluster-id and an accidental cluster-id collision, which fail in opposite directions

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.

The cluster-id is the loop-prevention key for a route reflector cluster. A route reflector prepends it to the CLUSTER_LIST attribute on every route it reflects, and discards any advertisement that arrives with its own cluster-id already in that list.

That is the whole mechanism, and it is two sentences long. The interesting part is the design consequence, which is not obvious and which operators discover the hard way: two RRs that share a cluster-id throw away each other’s reflected routes, so every client in the cluster must have a session to both of them. Get that wrong and the network works — until the day a client’s only RR reboots.

Where the cluster-id lives on VyOS 1.5

set protocols bgp system-as 65001
set protocols bgp parameters router-id 10.255.0.5
set protocols bgp parameters cluster-id 10.255.255.1

Both the router-id and the cluster-id sit under parameters in the 1.4/1.5 tree. The local AS is declared once with system-as, and neighbours hang off set protocols bgp neighbor ... without repeating it — the 1.3 shape that embedded the AS number in every line (set protocols bgp 65001 cluster-id ...) is rejected by commit on 1.5.

If you do not set a cluster-id, the RR uses its own router-id. That default is correct for a single-RR cluster and wrong for every redundant one, for a reason the next section makes concrete.

What a route reflector stamps, and when

An RR divides its internal peers into clients and non-clients, and reflects:

  • a route learned from a client to all other clients and to all non-clients;
  • a route learned from a non-client to all clients only.

On each of those reflections it adds two optional non-transitive attributes:

  • ORIGINATOR_ID — the BGP identifier of the router inside this AS that originated the route. Both RRs in a cluster set it to the same value, because it names the originating client, not the reflector.
  • CLUSTER_LIST — the reflector prepends its own cluster-id.

Both are stripped before the route is advertised to an external peer; they are internal bookkeeping and mean nothing outside the AS.

The check on receipt is the loop-breaker: an RR that finds its own cluster-id already in an advertisement’s CLUSTER_LIST ignores that advertisement entirely. Not “prefers it less” — ignores it.

The canonical two-RR cluster

Two RRs, one cluster-id, every client peering with both:

# RR-1
set protocols bgp system-as 65001
set protocols bgp parameters router-id 10.255.0.5
set protocols bgp parameters cluster-id 10.255.255.1
set protocols bgp neighbor 10.0.0.1 remote-as 65001
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-reflector-client
set protocols bgp neighbor 10.0.0.2 remote-as 65001
set protocols bgp neighbor 10.0.0.2 address-family ipv4-unicast route-reflector-client
set protocols bgp neighbor 10.255.0.6 remote-as 65001
# RR-2
set protocols bgp system-as 65001
set protocols bgp parameters router-id 10.255.0.6
set protocols bgp parameters cluster-id 10.255.255.1
set protocols bgp neighbor 10.0.0.1 remote-as 65001
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-reflector-client
set protocols bgp neighbor 10.0.0.2 remote-as 65001
set protocols bgp neighbor 10.0.0.2 address-family ipv4-unicast route-reflector-client
set protocols bgp neighbor 10.255.0.5 remote-as 65001

Different router-ids, one shared cluster-id, and 10.255.0.5 / 10.255.0.6 peering with each other as ordinary non-client iBGP neighbours. Note that route-reflector-client is a property of the address family, not of the neighbour: it is set under address-family ipv4-unicast, and a session that carries IPv4 and IPv6 needs the flag set in each family you intend to reflect.

Now follow a prefix from client 10.0.0.1:

sequenceDiagram
  participant A as Client 10.0.0.1
  participant RR1 as RR-1 (cluster 10.255.255.1)
  participant RR2 as RR-2 (cluster 10.255.255.1)
  participant B as Client 10.0.0.2
  A->>RR1: iBGP update, prefix X
  A->>RR2: iBGP update, prefix X
  RR1->>B: reflect X — Originator 10.0.0.1, Cluster list [10.255.255.1]
  RR2->>B: reflect X — Originator 10.0.0.1, Cluster list [10.255.255.1]
  RR1->>RR2: reflect X to non-client
  RR2->>RR2: own cluster-id in CLUSTER_LIST — ignore
  RR2->>RR1: reflect X to non-client
  RR1->>RR1: own cluster-id in CLUSTER_LIST — ignore

Two things in that diagram are worth stopping on.

Client 10.0.0.2 receives two copies, one from each RR. The cluster-id did not suppress a copy; both RRs reflected the route they each learned directly from 10.0.0.1. This is the redundancy: if RR-1 disappears, the copy from RR-2 is already in the client’s table and the client re-runs best path locally.

The RR-to-RR advertisements are both discarded. Each RR sees its own cluster-id in the list and ignores the update. That is correct and intended — but it means the RR-to-RR session carries no reflected routes at all, which leads directly to the rule below.

Which copy the client installs

The client has two copies that are identical in every attribute the originating router set. The tie-break runs a long way down before it finds a difference, and knowing where it lands saves an argument:

  1. Weight, local-preference, AS-path length, origin, MED, eBGP over iBGP, IGP metric to the next hop — all identical, because both copies describe the same route from the same originator with the same next-hop.
  2. Router-id, with ORIGINATOR_ID substituted when present. Both copies carry the same ORIGINATOR_ID — the originating client — so this ties too. This is where “the lowest router-id of the two RRs wins” would be true if the attribute were the reflector’s identity. It is not.
  3. CLUSTER_LIST length. Both are length 1 in this topology, so this ties as well. It is the step that decides a multi-cluster topology, where one copy has been reflected twice.
  4. The neighbour’s peer address. The lowest wins.

So in the canonical design the winner is decided by which RR has the lower peering address — an outcome nobody designed and nobody should depend on. If you need the clients to prefer a particular RR, express it with local-preference or weight in an import policy, which are compared at the top of the list rather than the bottom.

Reading it on the box

Read-only / SafeThe reflection attributes appear on their own line under the path
vyos@rr1:~$ show ip bgp 10.20.0.0/24
BGP routing table entry for 10.20.0.0/24
Paths: (2 available, best #1, table default)
Advertised to non peer-group peers:
10.0.0.2
Local
  10.0.0.1 from 10.0.0.1 (10.0.0.1)
    Origin IGP, metric 0, localpref 100, valid, internal, best (Peer Address)
    Last update: Tue Aug 19 09:14:22 2026
Local
  10.0.0.1 from 10.255.0.6 (10.255.0.6)
    Origin IGP, metric 0, localpref 100, valid, internal
    Originator: 10.0.0.1, Cluster list: 10.255.255.1
    Last update: Tue Aug 19 09:14:22 2026

Illustrative output

Three details to read out of that:

  • The Originator: and Cluster list: line appears only on the path that was reflected. The first path came straight from the client and carries neither.
  • from 10.255.0.6 (10.255.0.6) is the peer that sent this copy; the Originator is who put the route into the AS. Confusing the two is the commonest misreading of this output.
  • best (Peer Address) names the tie-break step that actually decided. When that reason is Peer Address, everything above it tied — which is the normal, expected outcome in a shared-cluster design and not a sign that anything is misconfigured.

The same fields are available from show ip bgp 10.20.0.0/24 json. Read the key names off your own image rather than hard-coding them into a parser: FRR’s JSON schema is stable in intent but has moved in detail across releases, and a monitoring check that silently stops finding a key reports health rather than an error.

Multi-cluster: different cluster-ids

Giving each RR its own cluster-id makes them separate clusters. The RR-to-RR advertisements are no longer discarded, so each RR re-reflects the other’s routes to its own clients:

# RR-1
set protocols bgp parameters cluster-id 10.255.255.1

# RR-2
set protocols bgp parameters cluster-id 10.255.255.2

A client of RR-2 now receives the prefix twice: once from RR-2’s own reflection of the copy it learned directly, with CLUSTER_LIST [10.255.255.2], and once from RR-2’s re-reflection of RR-1’s copy, with [10.255.255.2, 10.255.255.1]. Step 3 of the tie-break — CLUSTER_LIST length — is what separates them, and the singly-reflected copy wins.

The honest trade-off between the two designs is not “redundancy versus load balancing”. It is this:

Shared cluster-idDistinct cluster-ids
RR-to-RR reflectionsDiscardedAccepted and re-reflected
Client peering requirementEvery client must peer with every RR in the clusterA client may peer with one RR and still be reachable
Paths a client holdsOne per RR it peers withMore — its own RRs’ copies plus re-reflections
State and update churnLowerHigher, and it grows with the number of clusters
Typical useOne site, one cluster, full client peeringClusters that map to sites or regions

Neither is more correct. Choosing the shared cluster-id and then attaching clients to a single RR is what is wrong, and it is a combination that looks tidy on a diagram.

Failure modes

The cluster-id is set on one RR and not the other

The intended design was a shared cluster. RR-1 has an explicit cluster-id; RR-2 fell back to its router-id. The two RRs are now separate clusters, and they accept and re-reflect each other’s routes.

There is no loop — each RR still discards anything carrying its own cluster-id — but every client holds more copies than the design predicted, and the update churn is higher than it should be.

The dangerous part is what it conceals. A client attached to only one RR works fine in this state, because the other RR re-reflects. The misconfiguration is discovered when somebody “corrects” the cluster-id to match, and the singly-attached clients go dark at that commit.

Diagnostic: show configuration commands | match cluster-id on every RR, and compare. Before correcting it, audit client peering with show ip bgp summary on both RRs.

Two RRs in different clusters share a cluster-id by accident

The inverse, and it is worse. A configuration copied between sites carries the cluster-id with it. The two clusters now discard each other’s reflected routes, and the two halves of the AS cannot see each other’s prefixes.

This one fails silently and asymmetrically: prefixes that reach both halves by another path still work, so the outage is partial and looks like a routing policy problem.

Diagnostic: a prefix that is present on one RR and absent on the other with no policy explaining the difference. Confirm by comparing cluster-ids across sites, not just within a site.

Fix: allocate cluster-ids centrally, one per cluster, from a documented range.

route-reflector-client set on the RR-to-RR session

The two RRs are configured as each other’s clients. A route learned from a client is now reflected to a peer that also treats it as a client relationship, and the topology no longer matches the one you drew.

Diagnostic: show configuration commands | match route-reflector-client should list only client sessions. The other RR is a non-client; that is the whole distinction.

The cluster-id was changed on a live cluster

Changing it re-stamps every reflected route with a new CLUSTER_LIST, which means every client re-runs best-path selection over the whole table. On a large table that is a measurable event, not a free one.

Treat it as a maintenance-window change, do it on one RR at a time with commit-confirm, and confirm client tables have reconverged before touching the second.

Rollback

compare
commit-confirm 5
  • compare before the commit, every time — a cluster-id change is one short line with a fleet-wide effect, and it is easy to type into the wrong RR.
  • commit-confirm 5 for any change made over a session that traverses the cluster you are editing.
  • rollback N; commit; save reverts to a previous revision.
  • load /config/archive/<known-good-file>; commit; save restores a specific snapshot.

Rolling back a cluster-id costs a second full re-convergence, exactly like applying it did. Plan the window for two events, not one.

Production discipline

Cross-course references

The route-reflector lessons vyos-xxx-01-rr-concept and vyos-xxx-02-rr-config cover the reflection model and the base configuration this lesson extends; vyos-xxx-04-confederations covers the alternative scaling mechanism, and vyos-xxx-06-rr-troubleshoot walks the debugging of reflection failures. The BGP best-path lessons in Part XXVI cover the full selection order that this lesson enters at step 10. The OPNsense course’s XXX-OPNsense-DynamicRouting covers the same FRR mechanism on the firewall side.

Quiz

Knowledge check · 4 questions

  1. Q1. Two route reflectors serve one cluster. What cluster-id configuration does the canonical design use, and what does it require of the clients?

  2. Q2. The CLUSTER_LIST attribute is propagated to eBGP peers so the upstream can see which cluster reflected the route.

  3. Q3. The cluster-id was set on one RR and not the other. Explain what the network is actually doing, and why correcting it is more dangerous than leaving it.

    RR-1 has `set protocols bgp parameters cluster-id 10.255.255.1`. RR-2 has no cluster-id configured, so it uses its router-id, 10.255.0.6. The design document describes a single cluster with two RRs. Clients hold more paths than the document predicts and the RRs' update counters are higher than the other sites'. Several clients, added over the past year, peer with only one of the two RRs. Everything currently works. A change ticket has been raised to 'align the cluster-id on RR-2 with RR-1'.

  4. Q4. Two sites, each with its own RR cluster, cannot see each other's prefixes. The configurations look correct. Find the cause.

    Two sites each run a two-RR cluster, and the four RRs are fully meshed with iBGP. Site A's clients cannot see Site B's prefixes and vice versa, although each site's own prefixes are fine. There is no import or export policy on the inter-site sessions, the sessions are Established, and prefix counters on those sessions are non-zero in the received direction. Site B was built by copying Site A's RR configuration and editing the addresses.

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