Skip to content

WireGuard VPN

WireGuard is a modern VPN protocol built into the Linux kernel and supported natively by RouterOS v7+. It replaces complex negotiation protocols with a minimal, audited cryptographic design: Curve25519 key exchange, ChaCha20-Poly1305 encryption, BLAKE2s hashing. The result is a VPN that is simpler to configure, faster, and harder to misconfigure than IPsec or OpenVPN.

This guide covers interface setup, peer configuration, site-to-site tunnels, road warrior (remote access) configuration, and how WireGuard compares to IPsec.


A WireGuard interface is the local tunnel endpoint. RouterOS auto-generates a private/public key pair when the interface is created. You assign it a tunnel IP address, and it listens on a UDP port for encrypted traffic from peers.

A peer is a remote endpoint. Each peer has:

  • A public key — the peer’s identity (generated on their side)
  • An allowed-address list — which IP prefixes can be routed through this peer
  • An optional endpoint-address/port — where to send outbound packets

The allowed-address field does double duty: it controls which source addresses are accepted from the peer inbound, and which destination addresses are routed to the peer outbound. RouterOS installs these as kernel routing entries automatically.

When you add allowed-address=10.1.101.0/24,192.168.200.0/24 to a peer, RouterOS:

  1. Routes any packet destined for those prefixes out through the WireGuard interface to that peer.
  2. Accepts inbound packets from that peer only if their source falls within those prefixes.

Allowed-address ranges must not overlap between peers on the same interface. Overlapping ranges make routing ambiguous and will cause connectivity failures.

FeatureWireGuardIPsec (IKEv2)
Configuration complexityLow — interface + peersHigh — profile, peer, identity, proposal, policy
Key exchangeStatic keys (Curve25519)Dynamic (IKE negotiation)
Crypto agilityFixed modern primitivesConfigurable (risk of weak choices)
NAT traversalBuilt-in (UDP encapsulation)Requires NAT-T (UDP 4500)
RekeyingAutomaticIKE SA lifetime-based
InteroperabilityWireGuard-only peersIndustry-wide (Cisco, Juniper, etc.)
RouterOS supportv7.1+All versions
Audit footprint~4000 lines of codeVery large

Choose WireGuard for new RouterOS-to-RouterOS tunnels, remote access, and any situation where both endpoints can run WireGuard.

Choose IPsec when interoperating with third-party equipment (Cisco, Juniper, FortiGate), when your policy requires certificate-based authentication, or when running on RouterOS v6.


  • RouterOS 7.1 or later on all routers
  • UDP reachability between WAN IPs on the chosen listen port (default 13231 or 51820)
  • Public keys exchanged out-of-band between the two sides

Create the WireGuard interface. RouterOS generates the key pair automatically.

/interface/wireguard/add listen-port=13231 mtu=1420 name=wireguard1

Display the interface to get your public key — share this with the remote peer:

/interface/wireguard/print

Example output:

Flags: X - disabled; R - running
0 R name="wireguard1" mtu=1420 listen-port=13231
public-key="base64publickey..." private-key="(hidden)"

Assign a tunnel IP address:

/ip/address/add address=10.1.101.1/24 interface=wireguard1

Each remote endpoint is a peer entry. You need the remote side’s public key before proceeding.

/interface/wireguard/peers/add \
interface=wireguard1 \
public-key="REMOTE-PUBLIC-KEY-BASE64" \
allowed-address=10.1.101.2/32,192.168.200.0/24 \
endpoint-address=203.0.113.25 \
endpoint-port=13231 \
persistent-keepalive=25s

Key parameters:

ParameterPurpose
public-keyRemote peer’s identity (from their /interface/wireguard/print)
allowed-addressIPs routed to/accepted from this peer — include peer tunnel IP + remote LANs
endpoint-addressPeer’s WAN IP (or hostname); omit for dynamic clients
endpoint-portPeer’s listen port
persistent-keepaliveSend keepalive every N seconds — use 25s for NAT traversal

If only one side has a known public IP, set endpoint-address/endpoint-port only on the side that initiates. The passive side omits the endpoint and learns it from the first incoming packet.


Scenario: Office1 (WAN 192.168.99.1, LAN 10.0.0.0/24) ↔ Office2 (WAN 192.168.88.2, LAN 10.255.255.0/24). Tunnel IPs: 10.1.101.1 (Office1) and 10.1.101.2 (Office2).

/interface/wireguard/add listen-port=13231 mtu=1420 name=wireguard1
/ip/address/add address=10.1.101.1/24 interface=wireguard1
# Paste Office2's public key in allowed-address
/interface/wireguard/peers/add \
interface=wireguard1 \
public-key="OFFICE2-PUBLIC-KEY" \
allowed-address=10.1.101.2/32,10.255.255.0/24 \
endpoint-address=192.168.88.2 \
endpoint-port=13231 \
persistent-keepalive=25s
# Route Office2's LAN via the tunnel
/ip/route/add dst-address=10.255.255.0/24 gateway=wireguard1
# Allow incoming WireGuard UDP
/ip/firewall/filter/add \
chain=input protocol=udp dst-port=13231 \
action=accept comment="WireGuard" place-before=1
/interface/wireguard/add listen-port=13231 mtu=1420 name=wireguard1
/ip/address/add address=10.1.101.2/24 interface=wireguard1
/interface/wireguard/peers/add \
interface=wireguard1 \
public-key="OFFICE1-PUBLIC-KEY" \
allowed-address=10.1.101.1/32,10.0.0.0/24 \
endpoint-address=192.168.99.1 \
endpoint-port=13231 \
persistent-keepalive=25s
/ip/route/add dst-address=10.0.0.0/24 gateway=wireguard1
/ip/firewall/filter/add \
chain=input protocol=udp dst-port=13231 \
action=accept comment="WireGuard" place-before=1
# Check tunnel state and last handshake time
/interface/wireguard/peers/print detail
# Ping remote tunnel IP
/ping 10.1.101.2
# Ping remote LAN host
/ping 10.255.255.1

A recent last-handshake value (within ~3 minutes) confirms the tunnel is active.


Road warrior is the term for a VPN server that accepts connections from mobile clients with dynamic IPs. Each client gets a unique /32 allowed-address; the server never needs to know their endpoint in advance.

# Create WireGuard interface
/interface/wireguard/add listen-port=13231 mtu=1420 name=wireguard1
# Assign server tunnel IP
/ip/address/add address=172.16.0.1/24 interface=wireguard1
# Allow incoming WireGuard UDP
/ip/firewall/filter/add \
chain=input protocol=udp dst-port=13231 \
action=accept comment="WireGuard" place-before=1
# Masquerade WireGuard clients going to the internet
/ip/firewall/nat/add \
chain=srcnat out-interface=ether1 \
action=masquerade comment="WireGuard client NAT"
# Add a peer for each client — one peer per user
/interface/wireguard/peers/add \
interface=wireguard1 \
public-key="CLIENT1-PUBLIC-KEY" \
allowed-address=172.16.0.2/32
/interface/wireguard/peers/add \
interface=wireguard1 \
public-key="CLIENT2-PUBLIC-KEY" \
allowed-address=172.16.0.3/32

Note: no endpoint-address on the server — clients connect from dynamic IPs.

RouterOS can produce a ready-to-import config (including QR code) for each peer:

# Generate config for a specific peer
/interface/wireguard/peers/show-client-config \
[find where interface=wireguard1 and allowed-address="172.16.0.2/32"]

Add as-qr-code=yes to display a QR code for mobile apps.

The generated config includes:

  • Client’s private key (from the peer entry)
  • Server’s public key
  • Client tunnel IP
  • Server endpoint address and port
  • DNS (if configured)
ModeClient AllowedIPs
Split tunnel (LAN only)172.16.0.0/24, 10.0.0.0/24 — only VPN traffic through tunnel
Full tunnel (all traffic)0.0.0.0/0, ::/0 — all client traffic routed through server

Full tunnel requires the masquerade NAT rule above to be in place on the server.


When RouterOS receives an outbound packet, it checks the WireGuard peer table for a peer whose allowed-address contains the destination. If found, the packet is encrypted and sent to that peer’s endpoint.

When RouterOS receives an inbound WireGuard packet, it decrypts it and checks whether the source IP is in the sending peer’s allowed-address. If not, the packet is dropped.

# WRONG — both peers claim 10.0.0.0/8
/interface/wireguard/peers/add ... allowed-address=10.0.0.0/8 # peer 1
/interface/wireguard/peers/add ... allowed-address=10.0.0.0/8 # peer 2 — ERROR
# CORRECT — each peer owns distinct prefixes
/interface/wireguard/peers/add ... allowed-address=10.1.0.0/24 # peer 1
/interface/wireguard/peers/add ... allowed-address=10.2.0.0/24 # peer 2

RouterOS will reject or misbehave on overlapping entries on the same interface.


Minimum required firewall additions for WireGuard:

# Accept incoming WireGuard handshakes and data
/ip/firewall/filter/add \
chain=input protocol=udp dst-port=13231 \
action=accept comment="WireGuard inbound" place-before=1
# Allow established/related through forward chain
/ip/firewall/filter/add \
chain=forward connection-state=established,related \
action=accept comment="Forward established"
# Allow WireGuard clients to reach LAN
/ip/firewall/filter/add \
chain=forward in-interface=wireguard1 \
action=accept comment="WireGuard to LAN"

For IPv6 road warrior servers, add the same rules under /ipv6/firewall/filter.


# Interface state and public key
/interface/wireguard/print detail
# Peer state: last handshake, rx/tx bytes, endpoint
/interface/wireguard/peers/print detail

A peer is healthy if last-handshake is within ~3 minutes. If it shows never, the tunnel has not exchanged a handshake — check firewall and public keys.

/system/logging/add topics=wireguard,debug action=memory
/log/print where topics~"wireguard"
SymptomLikely causeFix
last-handshake: neverUDP port blocked or wrong endpointCheck firewall on both sides; verify endpoint-address
Handshake OK, no trafficMissing or wrong routeCheck allowed-address and /ip route print
Connection drops under NATUDP state timed outAdd persistent-keepalive=25s to peer
Partial connectivityOverlapping allowed-addressMake sure no two peers share prefix ranges
Fragmented large packetsMTU too highSet interface MTU to 1420

WireGuard adds 60 bytes of overhead (32-byte header + 28 bytes for IPv4/UDP). With a standard 1500-byte Ethernet path:

1500 (Ethernet) - 60 (WireGuard overhead) = 1420 bytes tunnel MTU
/interface/wireguard/set wireguard1 mtu=1420

RouterOS generates the private key automatically at interface creation. You cannot (and should not) set it manually. The public key is derived from the private key and is safe to share.

# Create interface — key pair generated automatically
/interface/wireguard/add name=wireguard1 listen-port=13231 mtu=1420
# Read the public key to share with remote peers
/interface/wireguard/print

If you need to rotate keys, remove and re-create the interface, then update all peers with the new public key.