OSPF is where most networks first meet dynamic routing, and it’s where RouterOS 7 made one of its most sensible breaks with the past. The v6 model — a network statement here, an interface entry there, half the config dynamic and unexplainable — is gone. RouterOS 7 has exactly three objects: an instance, one or more areas, and interface templates that decide where OSPF actually runs.
If you’re coming from Cisco or from the VyOS Guide on this site, the mapping is direct: instance is the process, area is the area, and interface-template replaces both network statements and per-interface OSPF configuration in one object.
The redesign also removed the excuse for sloppy OSPF. Templates force you to say explicitly which interfaces speak OSPF, at what cost, with what authentication. The rest of this article builds a clean two-router setup and then covers the failure mode that generates more forum threads than all others combined: neighbors stuck in ExStart.
The Three-Object Model
Instance — the OSPF process. Holds the router ID, version (2 for IPv4, 3 for IPv6), and redistribution policy:
/routing ospf instance add name=ospf-v2 version=2 router-id=10.255.0.1Set the router ID explicitly, always. Letting it auto-select from an interface address means your OSPF identity changes when addressing does — and duplicate router IDs are a spectacular way to corrupt an LSDB.
Area — belongs to an instance:
/routing ospf area add name=backbone instance=ospf-v2 area-id=0.0.0.0Stub and NSSA areas exist (type=stub, type=nssa) but most networks that fit on MikroTik hardware are fine as a single backbone area. Don’t add areas to look enterprise.
Interface template — the workhorse. A template matches interfaces either by name or by network prefix, and applies OSPF settings to whatever matches:
# Match by interface — deterministic, my preference/routing ospf interface-template add interfaces=ether1 area=backbone type=ptp cost=10
# Match by network — anything with an address inside the prefix joins/routing ospf interface-template add networks=10.0.12.0/30 area=backbone type=ptp cost=10networks= feels like the old network statement, but it’s a live matcher: add an address in that range to any interface and OSPF starts running there. Convenient in a lab, a liability in production. Matching on interfaces= says exactly what you mean. Pick one style and stay consistent.
A Working Two-Router Setup
R1 (10.255.0.1) and R2 (10.255.0.2), connected on a /30 transit, each with a LAN to announce:
# --- R1 ---/routing ospf instance add name=ospf-v2 version=2 router-id=10.255.0.1/routing ospf area add name=backbone instance=ospf-v2 area-id=0.0.0.0
# Transit link to R2/routing ospf interface-template add interfaces=ether1 area=backbone \ type=ptp cost=10
# LAN — advertise it, but never form adjacencies toward hosts/routing ospf interface-template add interfaces=bridge-lan area=backbone \ passiveR2 mirrors it with router-id=10.255.0.2. That’s the entire config. No network statements, no separate interface section.
Two deliberate choices in there:
type=ptp on the transit link. The default is broadcast, which runs DR/BDR election — pure overhead on a link with exactly two routers. Point-to-point skips the election, forms adjacency faster, and produces a cleaner LSDB. Use broadcast only where three or more routers genuinely share a segment. (Other types exist — nbma, ptmp, virtual-link — you’ll know when you need them.)
passive on the LAN. A passive template advertises the connected prefix without sending hellos on the interface. Every interface that faces hosts instead of routers should be passive — otherwise you’re multicasting hellos at workstations and waiting for someone to plug in a rogue OSPF speaker and win your default route.
Costs are plain integers (cost=10); lower wins. RouterOS won’t auto-derive cost from bandwidth, so establish a convention (10 for 10G, 100 for 1G, whatever) and apply it everywhere, or ECMP will surprise you.
Authentication: Use SHA, Not MD5
Unauthenticated OSPF means anyone on the segment can join your IGP and inject routes. RouterOS 7 supports proper HMAC-SHA (RFC 5709-style) on interface templates — auth= accepts simple, md5, sha1, sha256, sha384, sha512:
/routing ospf interface-template set [find interfaces=ether1] \ auth=sha256 auth-key="Xk29-long-shared-secret" auth-id=1auth-id is the key ID and must match on both ends, as must the algorithm and key. Mismatched auth is a quiet failure: neighbors simply never appear, and only the log (/log print where topics~"ospf") tells you why. simple and md5 exist for interop with old gear; for MikroTik-to-MikroTik links there is no reason not to use sha256.
Redistribution and Filtering
Redistribution lives on the instance. The redistribute= parameter takes a list of sources — static, connected, bgp, rip, dhcp, and friends:
/routing ospf instance set ospf-v2 redistribute=static,connectedLeft like this, it redistributes everything from those protocols as type-2 external LSAs. That’s rarely what you want — filter it. The instance’s out-filter-chain= runs every candidate LSA through a routing filter chain:
/routing ospf instance set ospf-v2 redistribute=static,connected \ out-filter-chain=ospf-out
# Only redistribute the statics we mean to/routing filter rule add chain=ospf-out \ rule="if (protocol static && dst in 10.20.0.0/16) { accept }"/routing filter rule add chain=ospf-out rule="reject"The filter rule language (the if (...) { accept } syntax) gets its own article later in this series — it’s shared between OSPF and BGP and worth learning once, properly. The one thing to internalize now: a chain with rules ends in an implicit reject, so an explicit final reject costs nothing and documents intent.
Default route origination is a first-class setting, not a redistribution hack:
# Advertise 0.0.0.0/0 only if we actually have one installed/routing ospf instance set ospf-v2 originate-default=if-installedalways is available and is exactly as dangerous as it sounds — a router advertising a default it can’t serve blackholes the whole area.
Verification
After every change, three prints:
# 1. Adjacencies — state should be Full/routing ospf neighbor print# instance=ospf-v2 router-id=10.255.0.2 address=10.0.12.2# state="Full" state-changes=6 adjacency=1h2m3s
# 2. The database — do you see the LSAs you expect?/routing ospf lsa print
# 3. The result — OSPF routes in the table/ip route print where ospfOn ptp links, Full is the only acceptable steady state. On broadcast segments, 2-Way with non-DR neighbors is normal — don’t “fix” it.
For live troubleshooting, watch the interface itself: /routing ospf interface print shows which interfaces matched a template, and if an interface you expected is missing, your template matcher (name vs network) is wrong.
Neighbors Stuck in ExStart: It’s the MTU
The classic OSPF failure on MikroTik, and it’s almost always the same root cause. Symptom: /routing ospf neighbor print shows the neighbor cycling through ExStart / Exchange and dropping back, forever. Hellos are fine (you can see the neighbor), full adjacency never forms.
Cause: MTU mismatch. OSPF database description packets carry the interface MTU, and the exchange fails when the two ends disagree — typically because one side is a VLAN or tunnel interface with a smaller MTU, or someone set jumbo frames on one switch port only.
# Compare both ends/interface print where name=ether1# look at actual-mtu on each routerThe fix is to make the MTUs match. Note that RouterOS has no mtu-ignore knob like Cisco IOS — you can’t tell it to skip the check, which is arguably correct: an MTU mismatch will eventually break something bigger than OSPF. Fix the interfaces, don’t paper over it. This bites hardest on EoIP/VXLAN/WireGuard transports where the tunnel MTU is smaller than Ethernet’s 1500 — set both tunnel endpoints’ MTU explicitly and OSPF follows.
Other ExStart/Exchange culprits, in descending order of likelihood: one side blocking IP protocol 89 in the firewall (allow it in input before the drop rules), duplicate router IDs, and a type=ptp / type=broadcast mismatch between the two ends.
Closing Thoughts
The v7 OSPF model rewards explicitness:
- Instance with a hand-set router ID; area; interface templates.
- Match templates on
interfaces=, notnetworks=, unless you genuinely want address-driven behavior. type=ptpon router-to-router links,passiveon everything facing hosts.auth=sha256everywhere — MD5 is for interop, not for choice.- Redistribute through a filter chain, never bare.
- ExStart loops are an MTU mismatch until proven otherwise.
Next up in this series: BGP on the new v7 stack, where MikroTik didn’t just reorganize the config — they rewrote the whole engine.