DEV Community

Hugo | DevOps | Cybersecurity
Hugo | DevOps | Cybersecurity

Posted on Originally published at valtersit.com

BIRD 2.x Anycast and Deterministic BGP Failover

At 03:14 on a Tuesday, a /32 anycast prefix began flapping between two PoPs. Both sites were "equally good" — same AS path length, same origin, no MED, no local_pref differentiation. BGP's tiebreaker landed on router ID, and traffic bounced between Frankfurt and Ashburn every 30 seconds for forty minutes while everyone stared at dashboards wondering why latency graphs looked like a seismograph. The fix was four lines of config. The detection took ninety minutes because nobody had alerting on route churn.

This guide is for senior sysadmins and network engineers running BGP in anger — people who already know what a peer group is and don't need "BGP is the protocol that makes the internet work" explained to them. By the end you'll be able to build anycast that fails over deterministically, not by luck, and you'll understand exactly where the sharp edges are.

Scope boundary: BIRD 2.x only. BIRD 1.x uses a different config dialect and is effectively legacy at this point. If you're still on 1.6, migrate first — everything below assumes 2.x semantics.

:::note[TL;DR]

  • Anycast is trivial; deterministic failover is the actual engineering problem. BGP best-path will happily pick a path you didn't intend.
  • Use BFD for fast link/node failure detection and health-checked static routes for application-level failures. They protect against different things.
  • local_pref is your lever (inbound, you control it); MED is advisory (outbound, your upstream decides whether to honor it). Prepend only after you've measured.
  • Every export filter ends in reject. Whitelist prefixes. The default-route leak is not a hypothetical. :::

Prerequisites

  • BIRD 2.x installed (bird2 package on Debian/Ubuntu; bird on RHEL-family via EPEL — check your distro, naming varies)
  • Root or CAP_NET_ADMIN for kernel route programming
  • At least one BGP session to an upstream or route reflector
  • Basic familiarity with birdc and the concept of routing tables vs. protocols
  • A health-check endpoint you actually trust

Why BIRD, and When You Shouldn't Use It

Picking a routing daemon is a fifteen-year decision. Get it wrong and you inherit a config dialect that fights you every time you need to express something non-trivial. BIRD's selling point is that its filter language is a real expression language — you can do arithmetic, string matching, set membership, and conditional logic without shelling out to Python. That matters enormously when you're encoding a steering contract across dozens of peers.

Here's the minimal skeleton. Notice the separation of concerns: device learns link state, kernel syncs to the FIB, static holds your anycast service IP, and bgp speaks to the world.

router id 192.0.2.1;

protocol device {
}

protocol kernel {
    ipv4 {
        export all;   # push BIRD's table into the kernel FIB
        import none;  # we don't want kernel routes back unless you know why
    };
    scan time 10;
}

protocol static anycast_v4 {
    ipv4;
    route 203.0.113.10/32 reject;  # placeholder; toggled by health check
}

protocol bgp upstream_a from bgp_template {
    neighbor 198.51.100.1 as 64500;
}
Enter fullscreen mode Exit fullscreen mode

The import/export direction is the number one thing people get backwards. import means "routes coming from the peer into my table." export means "routes going from my table to the peer." If you find yourself importing your own anycast prefix, you've inverted something.


⚠️ TRUNCATED VERSION
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): valtersit.com


🛠️ Partner Tools for Developers

ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.

➜ valtersit.com/deals/

Top comments (0)