Route Leaking Between VRFs on FortiGate: Why It's Trickier Than You Think

If you’ve spent any time designing multi-tenant networks, you know route leaking between VRFs is one of those invisible-until-it-isn’t problems. Most enterprise designs of any reasonable scale end up needing to share some routes between otherwise-isolated routing tables — shared DNS, monitoring, internet breakout, an M&A integration where the new business unit’s RFC1918 space overlaps with yours. On Cisco and Juniper the playbook is well-trodden. On FortiGate the same outcome is reachable, but the path is sufficiently different that many engineers — including some who are otherwise expert on the platform — never quite get there cleanly. This post is about why.

Why Route Leak in the First Place

Pure VRF isolation sounds clean: separate routing tables, separate forwarding decisions, no overlap, no leakage. In practice very few real-world topologies are pure. The patterns that force route leaking are predictable.

Shared services. You have a corporate VRF holding AD, DNS, syslog and monitoring infrastructure, and several customer-N VRFs that need access to those services but must not see each other. Each customer VRF needs to reach specifically the corporate-services subnets; the corporate VRF needs the return path.

Centralised internet breakout. Each tenant has its own VRF for segmentation but they all egress through a single firewall and proxy chain. The egress VRF needs to know how to send replies back to each tenant; each tenant needs a default into the egress VRF.

Acquisitions and migrations. The new entity’s address space overlaps yours. You spin up a VRF to keep their network isolated while you renumber, but specific subnets — the ones running the integration team’s services — must be visible from both sides during the transition.

Hub-and-spoke service insertion. The hub VRF runs IPS, sandbox detonation and DLP. Tenant VRFs send selected traffic into the hub for inspection then back out.

In all of these you don’t want to merge the VRFs — that breaks the segmentation that justified them in the first place. You want selective, explicit, auditable leakage of specific routes between specific VRFs.

What Makes FortiGate’s Approach Different

Here’s the bit that catches engineers out. On Cisco IOS-XE or NX-OS, route leaking has two distinct paths and both are first-class.

The static option:

ip route vrf RED 10.20.0.0 255.255.0.0 vrf BLUE 10.20.0.1

One line, no protocol involvement, route appears in RED’s table pointing at a next-hop reachable in BLUE’s table.

And the BGP-based option: configure Route Distinguisher and Route Target import/export under each VRF, redistribute the routes you want into MP-BGP, and the import-RT machinery moves them between VRFs.

On Junos, similarly: rib-groups for non-BGP leaking — define a primary RIB and a list of RIBs to also receive the routes, attach the rib-group to a protocol or static route. Works for OSPF, static, direct, anything. Or instance-import / instance-export policies for BGP, identical conceptually to Cisco’s RT-based approach.

On FortiGate, here’s the inconvenient truth: there is no static route leaking. You cannot write the FortiOS equivalent of Cisco’s ip route vrf X ... vrf Y .... The single mechanism for leaking routes between VRFs on a FortiGate is BGP. Even if your network has no BGP today — no eBGP peers, no iBGP fabric, no MP-BGP anywhere — if you want a route from VRF A to appear in VRF B, you need a BGP process running on the FortiGate to do it.

That single architectural fact is responsible for most of the confusion this feature generates.

How To Actually Do It

The mechanism FortiOS 7.0+ provides is two-fold: classic MP-BGP RT-based import/export, and a more direct leak-target shortcut that bypasses the BGP RIB for two VRFs on the same box. The Fortinet documentation covers both — see Route leaking between VRFs with BGP for the canonical version-specific syntax.

The shortest path to a working leak between two VRFs on a single FortiGate looks like this:

config router bgp
    set as 65001
    set router-id 1.1.1.1
    config vrf
        edit "10"
            set role pe
            set rd "65001:10"
            set export-rt "65001:10"
            set import-rt "65001:20"
            config leak-target
                edit "20"
                    set interface "port-vrf20"
                    set route-map "RM-LEAK-10-TO-20"
                next
            end
        next
        edit "20"
            set role pe
            set rd "65001:20"
            set export-rt "65001:20"
            set import-rt "65001:10"
        next
    end
end

A few things worth noting in that block.

The set role pe line tells FortiOS the BGP instance for that VRF is acting as a Provider Edge — it’s the trigger that turns on the MP-BGP machinery for VRF route exchange. Without it the rest is decoration.

The rd (Route Distinguisher) makes routes from each VRF unique within the BGP RIB. You can pick anything globally unique within your BGP fabric. The <ASN>:<number> convention is conventional but not strictly required.

export-rt and import-rt are the keys to which routes leak where. Routes from VRF 10 are tagged with RT 65001:10; any VRF importing that RT picks them up. Set up symmetrically to leak in both directions, asymmetrically for one-way leaks.

The leak-target block is FortiOS-specific. It says: forget the full MP-BGP exchange — for this specific cross-VRF leak, use this physical interface as the next-hop and apply this route-map to filter what crosses. It’s a shortcut useful when both VRFs are on the same box and you don’t want every leaked route bouncing through the BGP RIB.

You’ll also want a route-map gating what actually gets exported, because by default everything in the VRF’s table that BGP knows about goes out:

config router route-map
    edit "RM-EXPORT-10"
        config rule
            edit 1
                set match-ip-address "PL-SHARED-SERVICES"
                set action permit
            next
            edit 2
                set action deny
            next
        end
    next
end

Backed by a prefix-list:

config router prefix-list
    edit "PL-SHARED-SERVICES"
        config rule
            edit 1
                set prefix 10.10.10.0 255.255.255.0
                set ge 24
                set le 24
            next
        end
    next
end

Then attach the route-map to the export under the VRF’s BGP config: set export-route-map "RM-EXPORT-10".

Why This Is So Easy To Miss

Several reasons, all small individually but compounding.

The GUI doesn’t show you VRFs the way other vendors do. On a Cisco router with vrf definition CORP, the VRF is a top-level config object with everything hung off it. On a FortiGate, VRF is a per-interface tag (set vrf 10 under the interface) and a per-static-route filter, with no central “VRF” submenu in the GUI. Engineers new to multi-VRF on FortiGate often don’t realise it’s even active until something breaks.

Route leaking is not in the obvious place. You’d reasonably look under Static Routes for a static-leak option, or under VRF settings for a routing-leak option. It’s neither — it’s deep inside the BGP config, in a sub-block you only encounter if you go looking. The CLI path is config router bgp → config vrf → ... which is unintuitive if you don’t already know what you’re looking for.

The dependency on BGP isn’t documented prominently. The Fortinet docs assume you’ll use BGP. If you’ve never run BGP on a FortiGate, or never run BGP at all on this network, the natural reaction is “that’s not relevant to me” — and then you can’t find the static-leak that doesn’t exist. Cisco’s documentation by contrast leads with the static-route case.

The MP-BGP elements (RD, RT, role pe) feel like service provider configuration. They are, historically. To an enterprise engineer used to OSPF and a few static routes, the language of Provider Edge and Route Distinguishers feels off-platform — like you’ve stumbled into the wrong vendor’s manual. Even when you find the docs, there’s a credibility gap: surely this can’t be the way? But it is.

FortiOS version differences are real. The leak-target shortcut in particular has evolved across 7.0, 7.2, 7.4 and 7.6. Forum posts go stale fast. Whatever version you’re running, check the docs for that version rather than a generic search hit.

Comparison Across Vendors

For context, here’s how the same outcome — leak 10.10.10.0/24 from VRF RED into VRF BLUE — looks across platforms.

Cisco IOS-XE, static option:

ip route vrf BLUE 10.10.10.0 255.255.255.0 GigabitEthernet0/1

One line. Done.

Cisco IOS-XE, BGP / MP-BGP option:

vrf definition RED
 rd 65001:10
 route-target export 65001:10
 route-target import 65001:20
!
vrf definition BLUE
 rd 65001:20
 route-target export 65001:20
 route-target import 65001:10
!
router bgp 65001
 address-family ipv4 vrf RED
  redistribute connected
 exit-address-family
 address-family ipv4 vrf BLUE
  redistribute connected
 exit-address-family

Junos, rib-group option:

routing-options {
    rib-groups {
        leak-red-to-blue {
            import-rib [ RED.inet.0 BLUE.inet.0 ];
            import-policy LEAK-SHARED;
        }
    }
}

Attach the rib-group to OSPF, BGP, static — whatever’s importing the routes.

Junos, instance-import option for BGP:

routing-instances {
    BLUE {
        instance-type vrf;
        route-distinguisher 65001:20;
        vrf-import IMPORT-FROM-RED;
        vrf-export EXPORT-TO-RED;
    }
}

FortiOS — the BGP block from earlier in this post.

The FortiGate version isn’t longer than the Cisco MP-BGP option. It’s just less obvious that this is the answer, and it has no static-equivalent fallback for “I just want one route over there”.

Pitfalls That Bite In Production

Things to know before you deploy this for real.

Recursive lookups across VRFs. When a leaked route’s next-hop is in a different VRF, FortiOS may or may not handle the recursion the way you expect, depending on version. Test with diagnose ip route list per VRF and walk the path manually before trusting it.

FIB programming asymmetry. Just because the route appears in the routing table doesn’t mean it’s in the FIB. FortiOS RIB-vs-FIB divergence is rarer than on some platforms but does happen, particularly when policy-based routes interact with VRF leaks. Verify with diagnose ip address list and diagnose firewall proute list.

Policy still applies. Leaking a route to VRF B doesn’t grant traffic permission to flow there. Firewall policies between the source interface (in VRF A) and the destination interface (in VRF B) still need to permit the traffic. Engineers often think route leak equals traffic permitted, and spend hours debugging blocked flows that the routing table happily shows as reachable.

SNAT considerations. If leaked traffic needs source NAT — e.g., a shared-services VRF that doesn’t know how to route back to every tenant subnet — put the SNAT on the egress side at the cross-VRF policy. This is usually the cleanest design for shared internet breakout.

HA / FGSP. Cluster behaviour around VRF leak config has tightened since 7.0 but check your version, especially leak-target, which historically had edge cases on failover.

Closing Thought

Most route-leak failures I’ve seen on FortiGate aren’t because the engineer chose wrong; they’re because the engineer didn’t know they had a choice at all. The path is single (BGP), buried (sub-block of config router bgp), and uses MP-BGP vocabulary that feels foreign to enterprise networking. None of that makes the platform inferior — it just means the muscle memory built on Cisco doesn’t transfer cleanly.

If you take one thing from this post: when you need a route in another VRF on a FortiGate, your starting point is config router bgp → config vrf. Everything else follows.

For the canonical configuration reference including version-specific syntax, Fortinet’s documentation on route leaking between VRFs with BGP is the authoritative source — verify against the FortiOS version you’re actually running.