Two transit providers gives you redundancy the moment both sessions are up. Using that redundancy well — sending traffic out the cheaper or shorter path, pulling inbound traffic toward the link you want — is a separate skill, and it splits cleanly into two problems with very different difficulty.
Outbound is easy: you decide, locally, which exit to use. Inbound is hard: you are trying to influence other people’s routers, and you only get to ask politely.
Outbound: You Are in Control
Outbound path selection is a local decision, and local-preference is the biggest lever in BGP best-path — higher wins, before AS-path length even matters.
Prefer Transit A for everything, fall back to B:
# FRRroute-map FROM-TRANSIT-A permit 10 set local-preference 200route-map FROM-TRANSIT-B permit 10 set local-preference 100
router bgp 64500 neighbor 192.0.2.1 route-map FROM-TRANSIT-A in neighbor 198.51.100.1 route-map FROM-TRANSIT-B inPer-destination steering is just more specific matching. Send traffic to a particular network out B while keeping the default out A:
bgp as-path access-list TO-CONTENT permit _15169$route-map FROM-TRANSIT-B permit 5 match as-path TO-CONTENT set local-preference 300route-map FROM-TRANSIT-B permit 10 set local-preference 100Want to balance by destination popularity? Accept full tables from both, set equal local-pref, and let AS-path length pick the shorter path per prefix — the closest thing to “natural” load sharing without per-flow hashing across providers.
Inbound: You Can Only Ask
Inbound traffic follows the path other networks choose. You influence that choice with three increasingly blunt tools.
AS-Path Prepend
Make a path look longer so it loses on tie-breaks. Prepend your own AS on announcements out the link you want to de-prioritize:
route-map TO-TRANSIT-B-OUT permit 10 set as-path prepend 64500 64500router bgp 64500 neighbor 198.51.100.1 route-map TO-TRANSIT-B-OUT outPrepending is coarse. One prepend is often ignored (AS-path is far down the decision list, after local-pref the remote network sets for its own reasons). Two or three is typical. More than three rarely helps and signals you do not understand the problem.
MED
MED only influences a network you peer with on multiple links, telling it which of your links to prefer. It is comparable only between routes from the same neighbor AS, so it does nothing for choosing between two different transits:
route-map TO-TRANSIT-A-LINK2 permit 10 set metric 100 # higher MED = less preferred (lower wins)Useful for two links to the same provider; useless for steering between A and B.
Provider Communities — The Real Tool
The effective inbound lever is the BGP communities your upstream publishes. They let you reach past your provider to influence how their peers see you: prepend to specific upstreams, set local-pref inside the provider, or selectively not announce.
# Examples (each provider documents their own values):# 64500:70 -> provider sets local-pref 70 (lower) on this prefix# 64500:1:174 -> prepend once toward AS174# 64500:0:3356 -> do NOT announce to AS3356route-map TO-TRANSIT-A-OUT permit 10 set large-community 64500:1:174 64500:1:174 additiveThis is far more surgical than prepending, because it acts deep inside the provider’s network where the path decision is actually made. Read each upstream’s community guide — they differ, and this is where the real inbound control lives.
Splitting Inbound by Prefix
The bluntest but most reliable inbound control: announce more-specifics out the preferred link and the aggregate everywhere. Longest-prefix-match beats every BGP attribute, so the more-specific wins regardless of what remote networks prefer.
# Announce the /24 halves split across links, aggregate /23 out both# Out Transit A: 203.0.113.0/24 + 203.0.113.0/23# Out Transit B: 203.0.113.0/23 onlyUse sparingly. Deaggregation adds to the global table, and many networks filter long prefixes. It is the hammer for when prepend and communities are not enough — not the default.
The Decision Process, in Order
Every steering trick maps to one step of the BGP best-path algorithm. Knowing the order is the difference between a knob that works and one that gets silently overridden:
| Step | Attribute | Direction | Who sets it |
|---|---|---|---|
| 1 | Weight (Cisco-only) | Outbound | You, locally |
| 2 | Local-preference | Outbound | You, locally |
| 3 | AS-path length | Both | You influence via prepend |
| 4 | MED | Inbound (same AS) | You, via metric |
| 5 | eBGP over iBGP | — | Topology |
| 6 | IGP metric to next-hop | Outbound tie-break | Hot-potato |
Local-pref (step 2) beats AS-path (step 3), which is why a remote network’s local-pref policy quietly eats your prepends — their step 2 runs before your prepend reaches their step 3. Provider communities work because they reach in and change the provider’s own step 2, ahead of where prepend lives. That single fact explains why communities are surgical and prepend is a blunt instrument.
When a Transit Drops: Convergence and the Failover Trap
Redundancy is only real if failover behaves the way you assumed. Pull Transit A and watch outbound move to B:
# Simulate the session lossrouter bgp 64500 neighbor 192.0.2.1 shutdown
# Outbound should reconverge onto Transit Bshow bgp ipv4 unicast 8.8.8.0/24# best path now via 198.51.100.1
# How long did the RIB take to settle?show bgp ipv4 unicast summary | include 192.0.2.1Two production gotchas surface here. First, if you set Transit A to local-pref 200 and B to 100, outbound fails over cleanly — best-path just re-runs. But inbound does not move on your command at all: remote networks only stop using A when their session via A goes away and they reconverge on their own timescale, which can be tens of seconds with default timers. Enable BFD on the transit links so a dark-fibre cut is detected in milliseconds rather than waiting on the BGP hold timer:
# FRR — fast failure detection on the transit sessionneighbor 192.0.2.1 bfdSecond, watch the return path during single-homed operation. If you were splitting inbound with more-specifics out A only, those more-specifics vanish when A drops — inbound for that block falls back to the aggregate announced out B, which is correct, provided you actually announced the aggregate everywhere. The classic outage is announcing more-specifics out the preferred link and forgetting the aggregate on the backup; lose the preferred link and that traffic blackholes. Verify both links carry the covering aggregate before you rely on the split:
show bgp ipv4 unicast neighbors 198.51.100.1 advertised-routes | include 203.0.113.0/23Verifying It Does What You Think
# Which exit is chosen for a destination?show bgp ipv4 unicast 8.8.8.0/24# ">"-marked best path should be via the intended neighbor
# Confirm prepend/communities are actually applied outboundshow bgp ipv4 unicast neighbors 198.51.100.1 advertised-routesshow bgp ipv4 unicast 203.0.113.0/24For inbound, config is not proof — measure. Look at interface counters and flow data per provider, and use looking glasses in the regions you care about to see which path remote networks actually take:
# From a third-party looking glass, check the AS-path back to you# AS-path should traverse your preferred upstreamWatch for the Communities Being Stripped
A community-based inbound plan fails silently if the upstream strips the communities you set before they take effect. Confirm they survive onto the wire, and confirm the provider documents the exact action — a transit that resets local-preference on ingress will ignore a community telling it to lower local-pref:
# Are the large-communities actually attached to what we advertise?show bgp ipv4 unicast neighbors 192.0.2.1 advertised-routes 203.0.113.0/24show bgp ipv4 unicast 203.0.113.0/24# Large Community: 64500:1:174 64500:1:174 <- must be present hereIf the community is present on your side but the provider’s looking glass shows the path unchanged, you are either using the wrong value or hitting a provider that does not honor that action on your prefix class. Read their guide, not a generic one — 64500: here stands in for the provider’s own ASN, and the function-to-value mapping is theirs. There is no portable community standard for this beyond the informational well-known ranges; everything surgical is per-provider.
The Honest Limitation
You will never get inbound perfectly balanced. Remote networks weight their own decisions — peering relationships, cost, hot-potato routing — far above anything you signal. Aim for “good enough”: shift the bulk of inbound off a congested or expensive link, accept that some will not move, and re-measure. Operators who chase a perfect 50/50 inbound split with ever-more prepends are fighting physics. Set local-pref outbound, use provider communities inbound, measure, and stop when it is good enough.