MikroTik is everywhere on the WISP and small-ISP edge, and that edge speaks BGP. RouterOS 7 rewrote BGP from the ground up — new menu, new filter model — so even if you knew v6 BGP, this is worth relearning. Same dual-ISP build as the Cisco twin: two upstreams, path control both directions, automatic failover.
BGP is the internet's routing protocol — policy-driven, path-vector, built for peering between networks that don't trust each other. You run it on a MikroTik edge for the same reason as anywhere: two ISPs, your own prefix and ASN, and the need to survive an upstream dying automatically. What's MikroTik-specific is that RouterOS 7 completely replaced the v6 BGP implementation: it's now a single /routing bgp connection object per peer, and all policy lives in /routing filter rule chains rather than the old per-instance settings. Configs and tutorials written for v6 will not paste in.
The jobs are identical to the Cisco twin: bring up both eBGP sessions, filter so you never become accidental transit, steer outbound with local-preference and inbound with AS-path prepend. Only the syntax changes — and this is where a mixed estate benefits from one engineer who reads both.
01
One /routing bgp connection per ISP, our aggregate advertised via a network entry, and an output filter chain that permits only our own prefix — the same anti-transit rule as Cisco, expressed as a filter chain.
# The prefix we own and will advertise: /ip address add address=192.0.2.1/24 interface=lo-aggregate /routing bgp template set default as=65001 router-id=203.0.113.1 # Output filter: accept ONLY our aggregate, drop everything else # (so we never re-advertise one ISP's routes to the other). /routing filter rule add chain=BGP-OUT rule="if (dst==192.0.2.0/24) { accept }" add chain=BGP-OUT rule="reject" # The two eBGP sessions. local.role=ebgp, remote .as per ISP. /routing bgp connection add name=ISP-A remote.address=198.51.100.1 remote.as=64500 \ local.role=ebgp as=65001 output.filter-chain=BGP-OUT \ output.network=192.0.2.0/24 add name=ISP-B remote.address=203.0.113.9 remote.as=64501 \ local.role=ebgp as=65001 output.filter-chain=BGP-OUT \ output.network=192.0.2.0/24
Two traps in one. First, if you learned BGP on RouterOS 6 (/routing bgp instance, /routing bgp peer), none of it exists in v7 — it's /routing bgp connection and /routing filter rule now, and the filter syntax is a small scripting language, not a row of columns. Second, exactly like Cisco: with no output filter you re-advertise learned routes and become transit between your ISPs. The BGP-OUT chain permitting only your aggregate is not optional. Add an input max-prefix-limit on each connection as a circuit-breaker.
02
Higher local-pref wins for egress, set on the routes coming in from the preferred ISP. In v7 that's a rule in the connection's input filter chain.
/routing filter rule add chain=ISP-A-IN rule="set bgp-local-pref 200; accept" /routing bgp connection set ISP-A input.filter=ISP-A-IN # Egress now prefers ISP-A (local-pref 200 > default 100). Drop the # ISP-A session and its routes withdraw; ISP-B takes over automatically.
03
Make the path via ISP-B look longer so the internet prefers coming in via ISP-A. Prepend our own AS on the output toward ISP-B.
# Reuse a dedicated output chain for ISP-B that prepends before accepting # our aggregate. (Keep the anti-transit reject at the end.) /routing filter rule add chain=ISP-B-OUT rule="if (dst==192.0.2.0/24) \ { set bgp-path-prepend 3; accept }" add chain=ISP-B-OUT rule="reject" /routing bgp connection set ISP-B output.filter-chain=ISP-B-OUT # Our prefix now looks 3 hops longer via ISP-B -> inbound favours ISP-A.
Local-preference steers only your egress and never leaves your AS; AS-path prepend rides the advertisement and influences ingress — but it's a hint the rest of the internet may ignore if a remote network sits closer to ISP-B. For real inbound control you set BGP communities your ISP publishes (in v7, set bgp-communities in the output chain). Prepending is the portable default; communities are the scalpel. This is identical across both platforms — see the Cisco twin and the translation guide.
04
A small edge rarely needs the full table. Filter inbound to a default from each ISP, then confirm sessions, best path, and failover.
# Accept only 0.0.0.0/0 inbound to keep the RIB tiny (add to each IN chain): /routing filter rule add chain=ISP-A-IN rule="if (dst==0.0.0.0/0) { set bgp-local-pref 200; accept }" add chain=ISP-A-IN rule="reject" # --- Verify --- /routing bgp session print # both established, uptime climbing /ip route print where bgp # the chosen default (via ISP-A) /routing bgp advertisements print # what we're sending each peer # Failover: /interface disable ISP-A-link, watch the default flip to ISP-B.
| Symptom | Where to look |
|---|---|
| Session not established | TCP 179 reachability / AS mismatch / role — /routing bgp session print detail. |
| Pasting v6 config fails | You're on v7 — use connection + /routing filter rule, not instance/peer. |
| Egress via wrong ISP | local-pref rule not in the preferred peer's input chain. |
| Re-advertising ISP routes | Output filter chain missing or its final reject absent. |
Takeaways
/routing bgp connection + /routing filter rule chains. v6 configs don't apply; relearn it.reject.set bgp-local-pref in the preferred peer's input chain (higher wins, stays inside your AS).set bgp-path-prepend toward the backup — a hint; use bgp-communities for real control.NOCTIS builds and audits BGP edges on MikroTik and Cisco across Crete — v7-correct filters, real path control, and no accidental-transit surprises — for WISPs, multi-site businesses, and anyone with two upstreams.
Book a Discovery Call →