Subject: RouterOS v7: How to route LAN traffic via local WireGuard peer (3x-ui) without IP Forwarding/NAT on the server? (Keenetic-like setup)

Hello everyone,
I need some help configuring Policy-Based Routing (PBR) on RouterOS v7. I’ve spent hours trying to figure it out, but I can't seem to get the exact configuration right.
My Network Topology:

  • Router: MikroTik hEX refresh running RouterOS v7.23.1.
  • Local LAN: 192.168.88.0/24.
  • Home Server: Located inside the same LAN with a static IP 192.168.88.4. It runs a 3x-ui panel (handling downstream VLESS routing and load-balancing) and hosts an inbound WireGuard server.
  • Current Status: The WireGuard .conf profile has been successfully imported into the MikroTik router, and the handshake is established and active.

What I want to achieve:

  1. Redirect LAN traffic to VPN: All internet traffic from my local LAN devices must be routed through this WireGuard tunnel.
  2. Server Exception (Bypass): The home server itself (192.168.88.4) must always access the internet directly via the main ISP WAN interface to prevent a routing loop.
  3. Fail-open (Resiliency): If the WireGuard tunnel drops or the server goes offline, all LAN traffic must automatically fall back to the regular WAN interface directly, rather than being dropped (no blackholing).

Important Note on Implementation:
Previously, I had this exact setup running flawlessly on a Keenetic router—it took literally 4 clicks using its "Connection Priorities" (Policy Routing) feature. Crucially, IP Forwarding was NOT enabled on the home server, and I didn't configure any NAT/Masquerade on it. The server did not act as a traditional gateway for the network; the router handled everything.
I absolutely do not want to turn my home server into a full-blown gateway, enable packet forwarding, or mess with NAT on the Linux/3x-ui side.
How can I elegantly replicate this "Keenetic-style" behavior on RouterOS v7? Should I use /ip firewall mangle(with routing marks) or the newer /routing/rule (Routing Rules) paired with separate routing tables?
I would highly appreciate it if someone could share the exact terminal CLI commands to achieve this.
Thank you!

For this kind of setup, /routing/rule with a dedicated routing table is generally cleaner than the old mangle + routing mark approach, especially on RouterOS v7. The tricky part is that if your WireGuard endpoint is actually your local server at 192.168.88.4, then the server effectively becomes the next hop for the traffic and must be willing to forward packets. Without enabling IP forwarding on the Linux side, the server cannot pass traffic onward regardless of what the MikroTik does.

If your previous Keenetic setup worked without Linux forwarding enabled, there's a good chance the traffic was actually being sent to a remote WireGuard peer rather than the local server itself acting as a transit node. For fail-open behavior, you can use a separate routing table with check-gateway=ping or recursive routing so traffic automatically falls back to the main WAN if the WG route disappears.

I recently came across a similar networking discussion from Bizistech where the recommendation was to use RouterOS routing rules for policy routing and reserve mangle rules only for more complex traffic classification scenarios.

For this purpose, it's easier and more performant to use routing rules instead of mangle rules. First, if possible, connect to the router using WinBox and MAC address. In case you did something wrong with the routing rules, you might lock yourself out of WinBox using IP address!

Let's assume that the imported WireGuard configuration produces the WireGuard interface wg1, and the WireGuard IP address (inside the tunnel) of the remote peer is 10.20.30.40.

  • Check that you are able to run the ping command from the RouterOS Terminal to reach this address (adjust the IP address 10.20.30.40 and interface name wg1 accordingly):

    :ping count=5 10.20.30.40%wg1
    

    If this doesn't work, then you'll have to fix the WireGuard configuration first.

  • Also very important: make sure that the WireGuard peer setting in RouterOS has allowed-address=0.0.0.0/0 set.

    image

  • Add a routing table with FIB:

    /routing table
    add fib name=via-vpn
    
  • Assuming 10.20.30.40 is the remote gateway on the other side of the VPN (the address of the WG peer mentioned above), add the default route in the via-vpn table, using 10.20.30.40 as gateway:

    /ip route 
    add dst-address=0.0.0.0/0 gateway=10.20.30.40@main \
        check-gateway=ping routing-table=via-vpn
    
  • Add this rule at the top of the Routing -> Rules table:

    /routing rule
    add action=lookup min-prefix=0 table=main
    
  • Next, add this exception for the server 192.168.88.4 right below that rule:

    /routing rule
    add action=lookup-only-in-table src-address=192.168.88.4 table=main
    

    This rule makes sure packets originating from the server will always use the main routing table.

  • Now for every LAN/VLAN interfaces you have, add one routing rule below the rule above. For example, if you have a bridge bridge-local and a vlan50 interfaces and you want to route internet traffic of every clients connected to them through the VPN, add those routing rules below the ones above:

    /routing rule
    add action=lookup dst-address=0.0.0.0/0 interface=bridge-local table=via-vpn
    add action=lookup dst-address=0.0.0.0/0 interface=vlan50 table=via-vpn
    

    change lookup to lookup-only-in-table if you don't want to allow falling back to the main table when the VPN tunnel is down and always force the devices to use the via-vpn table only for internet connections!

  • Alternatively, if you don't want to use the source interface as criteria, but the source IP address of the clients, for example 192.168.88.0/24, 10.50.0.0/16, use routing rules like these (still placed after the min-prefix rule and the exception rule for src-address=192.168.88.4!):

    /routing rule
    add action=lookup src-address=192.168.88.0/24 table=via-vpn
    add action=lookup src-address=10.50.0.0/16 table=via-vpn
    
  • Don't forget to add the SRCNAT masquerade rule for traffic through the VPN tunnel (assuming the interface is named wg1):

    /ip firewall nat
    add action=masquerade chain=srcnat out-interface=wg1
    
  • The next step normally is needed to adjust the TCP MSS values of the connection going through the tunnel. For example, if the wg1 interface has the default 1420 MTU setting, these following mangle rules would be helpful:

    /ip firewall mangle
    add action=change-mss chain=forward comment="reduce MSS for WG" new-mss=1380 \
        out-interface=wg1 protocol=tcp tcp-flags=syn tcp-mss=1381-65535
    add action=change-mss chain=forward comment="reduce MSS for WG" new-mss=1380 \
        in-interface=wg1 protocol=tcp tcp-flags=syn tcp-mss=1381-65535
    

    Adjust the 1380 and 1381 values if the MTU of the WireGuard interface is not 1420. Use the formula MTU - 40 and MTU - 39 accordingly.

This is, however, something that I don't understand in your requirements. Unless you are using IPv6 only, if the remote WireGuard peer is the home server, then if that server wants to be able to direct the traffics coming from your LAN to the internet, it has to run some kind of IP forwarding and SRCNAT-ing. There is no magic around this. It could be that the forwarding & NAT-ing is built into the software that you run on it.

Thank you so much! Everything is working perfectly now. I really appreciate your help!