This is a well-known WireGuard behavior. WireGuard is designed to handle "roaming" — when it receives a valid authenticated packet from a peer at a new source IP, it updates its endpoint for that peer. However, your scenario creates a chicken-and-egg problem: router B won't update its endpoint until it receives a packet from router A's new IP, but if there's no outbound traffic from A (or keepalives aren't configured), B keeps sending to the stale address.
A few approaches for RouterOS:
On the WireGuard peer configuration on router A, make sure you have:
/interface wireguard peers set [find where endpoint-address="<router-b-ip>"] persistent-keepalive=25This should cause A to send keepalives from its new source IP, prompting B to update. If you already have this and it's still failing, the issue may be more subtle (perhaps related to how RouterOS handles the interface during route transitions).
You can create a script that monitors your routing state and restarts the WireGuard peer when a relevant change occurs. Here's a skeleton approach:
# Script: check-wg-source-ip
# Store the current source IP used to reach router B in a global variable
# If it changes, disable/enable the WireGuard peer
:local peerEndpoint "203.0.113.50" # Router B's IP
:local currentSrc [/ip route get [/ip route find dst-address="$peerEndpoint/32" active] gateway]
# Or more reliably, check which source IP would be used:
:local currentSrc [/ip route check $peerEndpoint as-value ->"src-address"]
:global lastWgSrcIP
:if ($currentSrc != $lastWgSrcIP) do={
:log warning "WireGuard source IP changed from $lastWgSrcIP to $currentSrc - restarting peer"
/interface wireguard peers disable [find where endpoint-address~"$peerEndpoint"]
:delay 1s
/interface wireguard peers enable [find where endpoint-address~"$peerEndpoint"]
:set lastWgSrcIP $currentSrc
}Then schedule it to run frequently:
/system scheduler add name=wg-source-monitor interval=10s on-event=check-wg-source-ipIf you have a known IP on the other side of the tunnel, you can use Netwatch to detect when it becomes unreachable and trigger a restart:
/tool netwatch add host=10.255.0.2 interval=10s timeout=2s \\
down-script="/interface wireguard peers disable [find]; :delay 2s; /interface wireguard peers enable [find]"This is reactive rather than proactive but catches the zombie state.
If router A has a stable public IP (or at least more stable than B), you might configure B with A's endpoint and have A with no endpoint configured for B — letting A be the "server" that B connects to. This inverts the problem.
Does router A have multiple WAN links or is this more about internal routing changes affecting the source IP selection? That might affect which approach is cleanest.
That points to a few more subtle possibilities:
RouterOS (like Linux) maintains connection tracking entries for UDP flows. Even after your route changes, the existing conntrack entry for the WireGuard UDP flow may keep the old source IP "pinned." The keepalives continue going out with the stale source because conntrack remembers the original tuple.
You can test this theory by checking:
/ip firewall connection print where dst-address~":51820"If you see the old source IP there after a route change, that's your culprit.
Possible fixes:
/ip firewall connection remove [find where dst-address~"<router-b-ip>"] /ip firewall raw add chain=prerouting protocol=udp port=51820 action=notrack
/ip firewall raw add chain=output protocol=udp port=51820 action=notrackThis forces every packet to go through fresh routing decisions.
Check your WireGuard interface configuration:
/interface wireguard print detailIf listen-address is set to a specific IP rather than 0.0.0.0 or left default, that could anchor the source. It should typically be unset or 0.0.0.0.
There can also be a race condition where the WireGuard interface sends its keepalive before the routing table has fully converged. This is harder to solve elegantly but the conntrack removal approach usually catches it.
Does your setup involve NAT anywhere in the path, or is this purely routed? And are both routers under your control, or is router B a third-party endpoint?