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, auto-generated interface keys, preshared keys, DNS, site-to-site tunnels, road-warrior (remote access) configuration, IPv6 dual-stack, 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 cause connectivity failures.

If endpoint-address and endpoint-port are set on a peer, RouterOS always sends to that fixed address. If omitted, RouterOS learns the endpoint from the first incoming authenticated packet — the standard pattern for road-warrior clients with dynamic IPs.

Runtime state is visible in read-only peer fields: current-endpoint-address, current-endpoint-port, last-handshake, rx, and tx.

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 detail

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

Interface properties:

PropertyDefaultDescription
nameInterface name
listen-portrandomUDP port for incoming connections
private-keyauto-generatedBase64 private key; auto-generated if empty
mtu1420Tunnel MTU (1420 avoids fragmentation on standard Ethernet paths)
disablednoEnable/disable interface

RouterOS generates the private key automatically when the interface is created. 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 detail

You cannot run a separate RouterOS command to generate an interface key pair. To rotate keys, remove and re-create the interface, then update all peers with the new public key.


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

Peer properties:

PropertyRequiredDescription
interfaceyesParent WireGuard interface
public-keyyesRemote peer’s public key (Base64)
allowed-addressyesIP ranges routed to and accepted from this peer
endpoint-addressnoPeer’s WAN IP or hostname; omit for road-warrior clients
endpoint-portnoPeer’s listen port; required if endpoint-address is set
persistent-keepalivenoKeepalive interval; use 25s to maintain NAT mappings
preshared-keynoShared symmetric key for post-quantum resistance
commentnoDescriptive label

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


A preshared key (PSK) adds a layer of symmetric encryption on top of the standard Curve25519 key exchange. This provides post-quantum resistance: even if an attacker later breaks the asymmetric cryptography, they cannot decrypt traffic that used a PSK.

PSKs are optional. When used, the same PSK value must be configured on both peer entries or the tunnel will not establish.

# Generate a random 32-byte Base64 key outside RouterOS
# Copy the value and use it as preshared-key on both sides
# Set PSK on peer entry — Router A
/interface/wireguard/peers/set [find public-key="ROUTER-B-PUBLIC-KEY"] \
preshared-key="BASE64-PSK-VALUE"
# Set the same PSK on peer entry — Router B
/interface/wireguard/peers/set [find public-key="ROUTER-A-PUBLIC-KEY"] \
preshared-key="BASE64-PSK-VALUE"

The PSK is a single Base64-encoded 32-byte value. Generate it with a trusted external tool, then copy the same value to both routers. There is no automated exchange — like public keys, PSKs are shared out-of-band.


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
/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
/ip/firewall/filter/add \
chain=input protocol=udp dst-port=13231 \
action=accept comment="WireGuard" place-before=1
/ip/firewall/filter/add \
chain=forward in-interface=wireguard1 \
action=accept comment="WireGuard forward in"
/ip/firewall/filter/add \
chain=forward out-interface=wireguard1 \
action=accept comment="WireGuard forward out"
/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/firewall/filter/add \
chain=input protocol=udp dst-port=13231 \
action=accept comment="WireGuard" place-before=1
/ip/firewall/filter/add \
chain=forward in-interface=wireguard1 \
action=accept comment="WireGuard forward in"
/ip/firewall/filter/add \
chain=forward out-interface=wireguard1 \
action=accept comment="WireGuard forward out"
# 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 describes 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
# Allow VPN clients to reach LAN
/ip/firewall/filter/add \
chain=forward in-interface=wireguard1 \
action=accept comment="WireGuard client forward"
# Masquerade WireGuard clients going to the internet (full-tunnel)
/ip/firewall/nat/add \
chain=srcnat src-address=172.16.0.0/24 \
action=masquerade out-interface-list=WAN \
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 \
comment="laptop-alice"
/interface/wireguard/peers/add \
interface=wireguard1 \
public-key="CLIENT2-PUBLIC-KEY" \
allowed-address=172.16.0.3/32 \
comment="phone-bob"

No endpoint-address on the server — clients connect from dynamic IPs and the server learns their address from the first authenticated packet.

WireGuard itself does not push DNS to clients. DNS is configured in the client profile. When generating client configs (see below), include the DNS server in the [Interface] section.

For full-tunnel road-warrior clients, the server’s tunnel IP (172.16.0.1) can act as the DNS resolver if /ip/dns is enabled on the router:

# Allow DNS queries from VPN clients to the router
/ip/firewall/filter/add \
chain=input in-interface=wireguard1 protocol=udp dst-port=53 \
action=accept comment="DNS from WireGuard clients"
/ip/firewall/filter/add \
chain=input in-interface=wireguard1 protocol=tcp dst-port=53 \
action=accept comment="DNS from WireGuard clients"

Set 172.16.0.1 (or your preferred DNS server) in the client config’s DNS field.

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

# Generate config for a specific peer
/interface/wireguard/peers/print detail where allowed-address="172.16.0.2/32"
# Generate wg-quick compatible config with QR code
/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 (iOS and Android WireGuard app).

The generated config includes:

  • Client’s private key
  • Server’s public key
  • Client tunnel IP
  • Server endpoint address and port
  • Preshared key (if configured)
ModeClient AllowedIPsEffect
Split tunnel172.16.0.0/24, 10.0.0.0/24Only VPN and LAN traffic goes through the tunnel
Full tunnel0.0.0.0/0, ::/0All client traffic is routed through the VPN server

Full tunnel requires the masquerade NAT rule above on the server.

Standard WireGuard client config (wg-quick format)

Section titled “Standard WireGuard client config (wg-quick format)”

For non-RouterOS clients (mobile apps, Linux wg-quick, Windows WireGuard):

[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 172.16.0.2/32
DNS = 172.16.0.1
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:13231
AllowedIPs = 0.0.0.0/0, ::/0 # full tunnel
# AllowedIPs = 172.16.0.0/24, 10.0.0.0/24 # split tunnel
PersistentKeepalive = 25

For a RouterOS device acting as a road-warrior client (e.g., a branch office with dynamic WAN IP):

/interface/wireguard/add name=wg-client listen-port=0
/interface/wireguard/peers/add \
interface=wg-client \
public-key="SERVER-PUBLIC-KEY" \
endpoint-address=vpn.example.com \
endpoint-port=13231 \
allowed-address=0.0.0.0/0 \
persistent-keepalive=25s
/ip/address/add address=172.16.0.4/32 interface=wg-client
# Default route via VPN (full tunnel)
/ip/route/add dst-address=0.0.0.0/0 gateway=wg-client

WireGuard supports IPv6 addresses on the tunnel interface and IPv6 prefixes in allowed-address. IPv6 peer transport (WAN) is also supported — current-endpoint-address accepts either IPv4 or IPv6.

# Assign IPv6 tunnel address on each side
/ipv6/address/add address=fd00:0:0:1::1/64 interface=wireguard1 advertise=no # Office A
/ipv6/address/add address=fd00:0:0:1::2/64 interface=wireguard1 advertise=no # Office B
# Office A peer entry includes Office B's IPv6 tunnel IP and LAN prefix
/interface/wireguard/peers/set [find public-key="OFFICE2-PUBLIC-KEY"] \
allowed-address=10.1.101.2/32,10.255.255.0/24,fd00:0:0:1::2/128,fd00:0:0:2::/64
# Add IPv6 route to remote LAN
/ipv6/route/add dst-address=fd00:0:0:2::/64 gateway=fd00:0:0:1::2

IPv6 firewall is separate from IPv4. Add rules under /ipv6/firewall/filter:

# Allow WireGuard UDP (if using IPv6 transport)
/ipv6/firewall/filter/add \
chain=input protocol=udp dst-port=13231 \
action=accept comment="WireGuard IPv6"
# Allow ICMPv6 (required for IPv6 operation)
/ipv6/firewall/filter/add \
chain=input protocol=icmpv6 \
action=accept comment="ICMPv6"
# Allow established/related
/ipv6/firewall/filter/add \
chain=input connection-state=established,related \
action=accept
# Allow WireGuard client forwarding
/ipv6/firewall/filter/add \
chain=forward in-interface=wireguard1 \
action=accept comment="WireGuard IPv6 forward"

# 1. 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
# 2. Allow WireGuard clients to reach LAN
/ip/firewall/filter/add \
chain=forward in-interface=wireguard1 \
action=accept comment="WireGuard to LAN"
# 3. Allow return traffic from LAN to WireGuard clients
/ip/firewall/filter/add \
chain=forward out-interface=wireguard1 \
action=accept comment="LAN to WireGuard"

Place these rules before any default-drop rules in the input and forward chains.

Restricting WireGuard input to known peers

Section titled “Restricting WireGuard input to known peers”

For site-to-site tunnels where both endpoints have static IPs, restrict the input rule to the known peer address:

/ip/firewall/filter/add \
chain=input protocol=udp dst-port=13231 \
src-address=203.0.113.25 \
action=accept comment="WireGuard from known peer"

To route only specific traffic through the tunnel (rather than relying solely on allowed-address):

# Create the policy-routing table first
/routing/table/add name=vpn-traffic fib
# Mark packets from a specific source host
/routing/mangle/add \
chain=prerouting src-address=192.168.1.50/32 \
action=mark-routing new-routing-mark=vpn-traffic
# Route marked packets through WireGuard
/ip/route/add dst-address=0.0.0.0/0 gateway=wireguard1 \
routing-table=vpn-traffic

When RouterOS sends 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 — CONFLICT
# 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.

RouterOS installs routes automatically from allowed-address, but explicit /ip route entries give more control and are visible in the routing table:

# Explicit static route (preferred for site-to-site — easier to manage)
/ip/route/add dst-address=10.255.255.0/24 gateway=10.1.101.2
# Check installed routes
/ip/route/print where gateway~"wireguard"

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

RouterOS does not provide an /interface/wireguard/monitor command. For runtime checks, generate traffic through the tunnel and re-run /interface/wireguard/peers/print detail to watch last-handshake, endpoint, and rx/tx counters update.

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.

Key runtime fields per peer:

FieldWhat to look for
last-handshakeWithin the last 3 minutes = session active
current-endpoint-addressMatches expected remote IP
rx / txCounters increasing = traffic flowing
/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
last-handshake: neverPublic key mismatchVerify keys match exactly on both sides
last-handshake: neverNo endpoint configuredAt least one peer must have endpoint-address set
Handshake OK, no trafficMissing or wrong routeCheck allowed-address and /ip route print
Handshake OK, no trafficForward chain dropAdd forward accept rule for in-interface=wireguard1
Connection drops under NATUDP state timed outAdd persistent-keepalive=25s to peer
Partial connectivityOverlapping allowed-addressEach peer must have unique, non-overlapping prefixes
MTU-related dropsFragmentationSet interface mtu=1420
PSK tunnel fails to establishPSK mismatchVerify identical PSK value on both peer entries

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
# 1. Ping the remote tunnel IP
/ping 10.1.101.2
# 2. Ping a remote LAN host
/ping 10.255.255.1
# 3. Check routes
/ip/route/print where gateway~"wireguard"
# 4. Check firewall rule hit counts
/ip/firewall/filter/print stats where comment~"WireGuard"