There are two ways to keep customer A’s traffic away from customer B’s on a shared router. The first is a wall of firewall rules that says “these subnets must never talk” — and works until someone adds rule 47 above rule 46 and it quietly doesn’t. The second is VRFs: separate routing tables where customer B’s routes simply do not exist in customer A’s world. No rule to get wrong. The packet has nowhere to go.
RouterOS 6 had VRF support that felt bolted on. RouterOS 7 rebuilt it on Linux VRF infrastructure, and it finally behaves like VRFs on real routing platforms: clean interface membership, per-VRF routing tables, route leaking that works, and VRF-aware tools. If you skipped VRFs on MikroTik because of v6 scars, it’s time to revisit.
The Model
Three pieces, and it helps to keep them straight:
VRF (/ip vrf) — a routing instance. Interfaces belong to it. Each VRF automatically gets a routing table of the same name.
Routing table (/routing table) — the actual table. VRF tables are created implicitly; you only create tables manually for policy routing (covered earlier in this series).
Interface membership — an interface lives in exactly one VRF. Its connected routes, and everything learned over it, land in that VRF’s table.
Basic Setup
Two customers on one router, overlapping address space and all:
# Create VRFs with member interfaces/ip vrf add name=cust-a interfaces=ether2,ether3/ip vrf add name=cust-b interfaces=ether4
# Addresses go on interfaces as usual — the VRF follows the interface/ip address add address=192.168.1.1/24 interface=ether2/ip address add address=192.168.1.1/24 interface=ether4Yes, the same subnet twice. That’s the point — the two 192.168.1.0/24 connected routes live in different tables and never meet:
/ip route print where routing-table=cust-a/ip route print where routing-table=cust-bStatic routes inside a VRF just name the table:
/ip route add dst-address=0.0.0.0/0 gateway=192.168.1.254 routing-table=cust-aDynamic routing works per-VRF too — OSPF instances and BGP connections take routing-table=/VRF parameters, so you can run a routing protocol with each customer without the sessions or routes bleeding into each other. The BGP article in this series covers the v7 connection model; add routing-table=cust-a to the connection and you have per-VRF BGP.
Route Leaking
Pure isolation is easy. The real world wants “isolated, except for the shared services subnet.” RouterOS 7 leaks routes by resolving a gateway in another VRF with the @ syntax:
# Give cust-a a route to shared services (10.99.0.0/24 lives in main)/ip route add dst-address=10.99.0.0/24 gateway=10.3.0.1@main routing-table=cust-a
# And main needs a route back into cust-a/ip route add dst-address=192.168.1.0/24 gateway=ether2@cust-a routing-table=mainTwo things to internalize:
Leaking is directional. One route gets traffic there, the second gets replies back. Forget the return route and you’ll stare at one-way packet captures wondering why TCP won’t establish.
The gateway can be an interface, not just an IP — gateway=ether2@cust-a resolves out that interface in that VRF. For point-to-point handoffs this is cleaner than picking a peer address.
There’s also the % form for pinning a gateway to an interface within a table (gateway=10.3.0.1%ether2), which solves ambiguity when the same next-hop subnet exists on multiple interfaces.
Leaked routes still don’t bypass the firewall — forward chain rules see inter-VRF traffic like any other. VRFs give you topology-level separation; the firewall still polices what you deliberately connected. Use both.
The Management VRF Pattern
The classic reason to want VRFs on an edge box: put the management plane in its own VRF so that no routing mistake in the customer-facing tables can ever expose or unreach your management access.
/ip vrf add name=mgmt interfaces=ether5/ip address add address=172.16.255.10/24 interface=ether5/ip route add dst-address=0.0.0.0/0 gateway=172.16.255.1 routing-table=mgmtNow the caveats, because this is where RouterOS VRFs demand attention:
Services don’t automatically listen per-VRF. /ip service has a vrf parameter in v7 — set it explicitly:
/ip service set ssh vrf=mgmt/ip service set winbox vrf=mgmtA service bound to one VRF stops answering in others. That’s exactly what you want for management — and exactly what will lock you out if you set it before the mgmt path actually works. Test from the management network before you flip winbox over, and know your recovery options (the RoMON article later in this series exists for a reason).
Router-originated traffic uses main by default. DNS lookups, NTP, fetch, RADIUS — they resolve in the main table unless the feature is VRF-aware. Check each service’s VRF support in the docs for your version before assuming; this area has improved release by release through 7.x, but “the router can’t reach RADIUS because RADIUS is only routable in mgmt” is still a classic.
VRF-Aware Troubleshooting
The tools take a vrf or table argument — use it, or you’re testing the wrong universe:
/ping 192.168.1.10 vrf=cust-a/tool traceroute 192.168.1.10 vrf=cust-a/ip route check 192.168.1.10 routing-table=cust-a/ip route check is underrated: it tells you which route a destination would resolve to in a given table without sending a packet. When leaking doesn’t work, check both directions with it — nine times out of ten the return path is the missing half.
For connectivity from the router out of a specific VRF (fetch, ssh), look for the per-command vrf parameter; where a command lacks one, that traffic originates in main — plan accordingly.
When VRF Beats Firewall Separation
Use VRFs when: address space overlaps (firewall rules can’t fix ambiguity), when isolation must survive human error in rule ordering, when customers/tenants bring their own routing (per-VRF BGP/OSPF), or when the management plane must be structurally unreachable from production networks.
Stick with plain firewall separation when: it’s one network with a guest VLAN. A VRF for a home guest network is complexity without payoff — the firewall article’s interface-list approach does it in three rules.
The mental shift: firewalls filter paths that exist. VRFs decide which paths exist at all. Defense in depth means using the second layer where the stakes justify it, not everywhere.
Closing Thoughts
VRFs in RouterOS 7 are finally boring in the good way — create the VRF, assign interfaces, leak exactly what you mean to, bind management services where they belong. The discipline that keeps them boring: every leak is a documented pair of routes (there and back), every service binding is tested before it’s trusted, and ping without vrf= on a VRF’d box is treated as a bug in your own debugging. I run the same pattern on VyOS — the concepts transfer one-to-one, and the VyOS VRF article in that series makes a good comparison read.