Bird Internet Routing Daemon (BIRD) is one of the most widely deployed open-source routing daemons, powering BGP sessions on bare-metal networks, Kubernetes CNI plugins, and IXP route servers worldwide. When a BGP session drops or a route disappears, Bird usually silently recovers or fails-safe — leaving operators unaware until traffic takes the wrong path or stops flowing entirely.
This tutorial shows you how to build complete observability for Bird deployments using Vigilmon: monitoring BGP session state, protocol health, and the Bird daemon process itself.
Why Bird needs external monitoring
Bird's self-healing mechanisms are also its blind spots:
- BGP session flaps — a session can drop and re-establish dozens of times before Bird gives up. Each flap re-converges the routing table, causing micro-outages that are invisible unless you're watching.
- Stuck sessions — a BGP peer can remain in
Activestate indefinitely, waiting for a TCP connection that never arrives, while Bird logs nothing actionable. - Route leaks and missing prefixes — a peer might be
Establishedwhile advertising zero routes, or more routes than expected, due to a filter misconfiguration. Session state alone doesn't tell you. - Protocol-level failures — OSPF or static protocol failures may not affect BGP uptime but still silently break reachability for specific subnets.
- Process failure —
birdandbird6are separate daemons. Either can crash independently.
Monitoring Bird means watching protocol state, route counts, and process health — not just whether the binary is running.
What you'll need
- A running Bird installation (Bird 1.x or Bird 2.x)
- The
birdccommand-line socket control tool (bundled with Bird) - A free Vigilmon account
Step 1: Check Bird protocol and BGP session state
Bird's control socket (birdc) gives you real-time protocol status. Build a probe that checks all BGP sessions are Established:
#!/bin/bash
# bird-bgp-probe.sh
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID"
LOG="/var/log/bird-probe.log"
# For Bird 2: use 'birdc'; for Bird 1: may be 'birdc' or 'birdc6'
BIRDC="birdc"
# Check Bird daemon is running
if ! systemctl is-active --quiet bird; then
echo "$(date): FAIL — bird service not running" >> "$LOG"
exit 1
fi
# Get all BGP protocol states
bgp_output=$($BIRDC show protocols all 2>/dev/null | grep -E "^[[:space:]]*BGP|State:|Routes:")
# Check for any non-Established sessions
if echo "$bgp_output" | grep -q "State:[[:space:]]*Active\|State:[[:space:]]*Idle\|State:[[:space:]]*Connect"; then
echo "$(date): FAIL — one or more BGP sessions not Established" >> "$LOG"
echo "$bgp_output" >> "$LOG"
exit 1
fi
# All sessions healthy — ping heartbeat
curl -s "$HEARTBEAT_URL" --max-time 10 --retry 2
Make it executable and add to crontab:
chmod +x /opt/scripts/bird-bgp-probe.sh
* * * * * /opt/scripts/bird-bgp-probe.sh
In Vigilmon:
- Monitors → New Monitor → Heartbeat
- Name:
Bird BGP Sessions - Interval:
5 minutes
Step 2: Monitor individual BGP peer health with route counts
A BGP session in Established state can still be broken if it's advertising or receiving zero prefixes. Add route-count checks per peer:
#!/bin/bash
# bird-route-probe.sh
# Checks that each critical BGP peer is Established AND has routes
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_ROUTE_HEARTBEAT"
LOG="/var/log/bird-route-probe.log"
# Define your critical BGP peers (protocol names as seen in 'birdc show protocols')
CRITICAL_PEERS=("upstream1" "upstream2" "peer_as65001")
MIN_ROUTES=1 # minimum expected imported routes
all_ok=true
for peer in "${CRITICAL_PEERS[@]}"; do
output=$(birdc show protocols all "$peer" 2>/dev/null)
state=$(echo "$output" | grep -oP '(?<=BGP state:\s{1,10})\w+')
imported=$(echo "$output" | grep -oP '(?<=Import updates:\s{1,10})\d+' | head -1)
if [ "$state" != "Established" ]; then
echo "$(date): FAIL — $peer state is '$state'" >> "$LOG"
all_ok=false
continue
fi
# Check route table for this peer
route_count=$(birdc show route protocol "$peer" count 2>/dev/null | grep -oP '\d+(?= routes)')
if [ -z "$route_count" ] || [ "$route_count" -lt "$MIN_ROUTES" ]; then
echo "$(date): FAIL — $peer established but route count is '$route_count'" >> "$LOG"
all_ok=false
fi
done
if $all_ok; then
curl -s "$HEARTBEAT_URL" --max-time 10 --retry 2
fi
*/5 * * * * /opt/scripts/bird-route-probe.sh
Step 3: Expose a Bird status HTTP endpoint
For integrations with Vigilmon's HTTP monitors, run a lightweight status server on your routing host:
#!/usr/bin/env python3
# bird-status-server.py — serves Bird health over HTTP
import subprocess
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
def get_bird_status():
try:
# Check service
svc = subprocess.run(["systemctl", "is-active", "bird"],
capture_output=True, text=True, timeout=5)
if svc.stdout.strip() != "active":
return {"status": "down", "reason": "service not active"}
# Check for non-Established BGP sessions
out = subprocess.run(["birdc", "show", "protocols"],
capture_output=True, text=True, timeout=10)
lines = out.stdout.splitlines()
problem_peers = [l.strip() for l in lines if "BGP" in l and "Established" not in l and "---" not in l]
if problem_peers:
return {"status": "degraded", "problem_peers": problem_peers}
return {"status": "ok"}
except Exception as e:
return {"status": "error", "reason": str(e)}
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/health":
status = get_bird_status()
code = 200 if status["status"] == "ok" else 503
body = json.dumps(status).encode()
self.send_response(code)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(body)
else:
self.send_response(404)
self.end_headers()
def log_message(self, *args):
pass
if __name__ == "__main__":
HTTPServer(("127.0.0.1", 9179), Handler).serve_forever()
Run as a systemd service, then add an HTTP monitor in Vigilmon:
- URL:
http://YOUR_ROUTER_IP:9179/health - Expected status:
200 - Name:
Bird Routing Health
Step 4: Monitor the Bird daemon process directly
Add a TCP port check to confirm Bird's control socket is accepting connections, and a process heartbeat for belt-and-suspenders coverage:
#!/bin/bash
# bird-daemon-check.sh
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_DAEMON_HEARTBEAT"
# Try to communicate with Bird via birdc
if birdc show status 2>/dev/null | grep -q "BIRD.*ready"; then
curl -s "$HEARTBEAT_URL" --max-time 10
fi
* * * * * /opt/scripts/bird-daemon-check.sh
In Vigilmon:
- Monitors → New Monitor → Heartbeat
- Name:
Bird Daemon Process - Interval:
2 minutes— daemon crashes should alert quickly
Step 5: Monitor Bird configuration reload jobs
If you use automation to push Bird configs and reload (birdc configure), track those jobs with a heartbeat:
#!/bin/bash
# bird-config-reload.sh — called by your config management tool
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_CONFIG_HEARTBEAT"
# Validate config first
if ! bird -p 2>/dev/null; then
echo "$(date): Config parse FAILED — not reloading" >> /var/log/bird-deploy.log
exit 1
fi
# Apply config
result=$(birdc configure 2>&1)
if echo "$result" | grep -q "Reconfigured"; then
curl -s "$HEARTBEAT_URL" --max-time 10
echo "$(date): Config reload successful" >> /var/log/bird-deploy.log
else
echo "$(date): Config reload FAILED: $result" >> /var/log/bird-deploy.log
exit 1
fi
Set the heartbeat interval to slightly longer than your expected reload cadence — if your automation runs hourly, set 90 minutes. A missed heartbeat means a missed reload.
Step 6: Configure alerting
Route Bird alerts to your network operations team:
- Alert Channels → Add Channel → Slack — use your
#nocor#networkingchannel - Add Channel → PagerDuty — for BGP session loss in production
- Assign all Bird monitors to both channels
Set 0-minute escalation for BGP session and route monitors — a missing route can affect end-user traffic within seconds.
Step 7: Build a routing infrastructure status page
- Status Pages → New Status Page
- Name:
Routing Infrastructure - Add monitors: Bird Daemon, BGP Sessions, Route Counts, Config Reload
- Set visibility to team-only unless you want public transparency
- Post the URL in
#noc
Complete monitoring reference
| Monitor | Type | What it catches | |---------|------|-----------------| | Bird Daemon Process | Heartbeat | Daemon crash, socket unavailable | | Bird BGP Sessions | Heartbeat | Session flap, stuck Active/Idle state | | Per-peer Route Counts | Heartbeat | Empty RIB, filter misconfiguration | | Bird Routing Health | HTTP | Aggregated status endpoint | | Config Reload Job | Heartbeat | Missed or failed config deployment |
Troubleshooting common failures
- BGP session stuck in
Active— check TCP connectivity:telnet <peer-ip> 179. If it hangs, a firewall is blocking BGP port 179. Runbirdc show protocols all <peer>for the full session log. - Session
Establishedbut zero routes — runbirdc show route protocol <peer>. If empty, check your import filter:birdc show protocols all <peer>will showImport rejectedcounters. birdcreturns "Can't connect to Bird socket" — Bird crashed or the socket path changed. Checksystemctl status birdand verifyRuntimeDirectoryin the Bird systemd unit.- Config reload returns
"Reconfiguration in progress already"— Bird is still applying a previous config. Wait 10–30 seconds and retry; if it persists, check for a Bird lock file.
Next steps
- RPKI validation — if you use Bird with an RTR server for RPKI, add a separate heartbeat that checks the RTR protocol state with
birdc show protocols rtr1 - BGP path selection logging — extend the probe to alert when a preferred path changes (
birdc show route for <prefix> allchanges its best-path source)
Start monitoring your Bird routing infrastructure today at vigilmon.online — free, no credit card.