Mullvad WireGuard VPN in OPNsense: Full IPv4 + IPv6 Dual-Stack Setup
Complete dual-stack implementation guide for OPNsense 25.7.3 with Mullvad's WireGuard VPN, covering IPv4 and IPv6 routing, gateway configuration, NAT rules, firewall policies, and automated DHCP provisioning for transparent network-wide VPN connectivity.
First published:
3 October 2025.
The objective is to create a transparent VPN gateway with OPNsense where all client traffic is automatically routed through Mullvad's WireGuard service without requiring individual device configuration. In my last blog post I looked at how to set up Mullvad's WireGuard VPN in pfSense with fully functioning IPv4 and IPv6 connectivity. OPNsense has some subtle, or not so subtle, differences and I have not yet settled on whether to use OPNsense or pfSense in my personal setup. Thus, I decided to write another post on how to set up Mullvad's WireGuard VPN in OPNsense with fully functioning IPv4 and IPv6 connectivity.
This setup uses three interfaces: WAN for upstream connectivity, LAN for management access, and INT for client devices that will have their traffic seamlessly routed through the VPN with full IPv4 and IPv6 support.
Tested and verified on OPNsense 25.7.3_7-amd64.
Internal interface configuration
The interface INT
that is connected to the clients has been set up with Static IPv4 as the IPv4 Configuration Type and Static IPv6 as the IPv6 Configuration Type. Under Static IPv4 Configuration and Static IPv6 Configuration private networks have been configured. Both IPv4 gateway rules and IPv6 gateway rules are set to Disabled. Enable interface is checked.
Configuring the Mullvad account and obtaining IP addresses
This part is identical to my previous blog post. Here, we essentially set up the credentials.
First, we will generate our key pair for use with WireGuard and upload the public key to Mullvad. There we will obtain our IPv4 and IPv6 addresses for use within the tunnel. The key pair as well as the IP addresses in this blog post serve as placeholders and you need to replace them with your own. Only the gateways presented in this section are the default Mullvad gateways across different configurations.
Generate a key pair with the following command:
| AOnnPfyXon01+WpNubM9FtSIn3s0PMHd1hPuM0vRokM= 8oXRo5Gx0351bjVEOY/VBQLVq1zQk79AoFr0cBRi7xY=
The first key is your private key and the second key is your public key.
Log in to your account and go to Account Management > Devices > Advanced.
Copy your public key into the Public Wireguard Key field and hit Upload. You will receive a name, an IPv4 and an IPv6 address. You can ignore the name, but write down the IPv4 and IPv6 addresses. Let's collect the data we have so far.
# WireGuard Keys private_key: AOnnPfyXon01+WpNubM9FtSIn3s0PMHd1hPuM0vRokM= public_key: 8oXRo5Gx0351bjVEOY/VBQLVq1zQk79AoFr0cBRi7xY= # Mullvad Assigned Addresses ipv4_address: 10.69.123.45/32 ipv6_address: fc00:bbbb:bbbb:bb01::5:4321/128 # Gateways ipv4_gateway: 10.64.0.1 ipv6_gateway: fc00:bbbb:bbbb:bb01::1
The gateways weren't shown in the pop up, but they are actually Mullvad's default gateways and can be found in their WireGuard configuration files. You can log out now.
Set up the WireGuard interface in OPNsense
We have the necessary credentials and IP addresses for use with WireGuard. In this section, we will configure the WireGuard interface, the upstream servers (peers) and the gateways to use with Mullvad. This part is slightly simpler than it was for pfSense. Also, we do not have to install WireGuard as it comes preinstalled with OPNsense.
Go to VPN > WireGuard > Instances and hit the plus sign.
As Name I like to choose «Mullvad», but you can enter whatever you like. Enter the Public key and Private key that you generated in the first step. I also like to set the Listen port to
51820
. Under Tunnel address enter the Mullvad assigned addresses, in this case10.69.123.45/32
for IPv4 andfc00:bbbb:bbbb:bb01::5:4321/128
for IPv6. Check Disable routes and hit Save. Ensure Enable WireGuard is checked and hit Apply.Go to Interfaces > Assignments. There should be a
wg0
(WireGuard - Mullvad) device. I like to addVPN
as the Description and hit Add.We will now first add Mullvad's IPv4 gateway, then add the IPv6 gateway. Go to System > Gateways > Configuration and click the plus sign. As Name I like to choose «Mullvad4» for the IPv4 gateway, but you can pick whatever you like. The Interface to select is
VPN
, the Address Family is IPv4 and the IP Address is the IPv4 address of Mullvad's gateway, i.e.10.64.0.1
. Remember to check Far Gateway as the gateway is (obviously) not in the/32
subnet. Then hit Save.Repeat for IPv6, where the Address Family is IPv6 and the IP Address is the IPv6 address of Mullvad's gateway, i.e.
fc00:bbbb:bbbb:bb01::1
. Hit Save then Apply.Next, we will add the remote server as our WireGuard peer. Select any WireGuard server you like from Mullvad's server overview. As an example in this blog post, I chose the server
fi-hel-wg-102
located in Helsinki, Finland:domain_name: fi-hel-wg-102.relays.mullvad.net ipv4_address: 193.138.7.157 ipv6_address: 2a02:ed04:3581:2::f001 public_key: xeHVhXxyyFqUEE+nsu5Tzd/t9en+++4fVFcSFngpcAU=
Go to VPN > WireGuard > Peers and click the plus sign. As Name I like to pick the name of the Mullvad server, i.e.
fi-hel-wg-102
. As Public key enter the server's public key, i.e. in this casexeHVhXxyyFqUEE+nsu5Tzd/t9en+++4fVFcSFngpcAU=
. Do not set a Pre-shared key, set Allowed IPs to0.0.0.0/0
and::/0
. As Endpoint address I like to select the IPv4 address, in this case193.138.7.157
, but you can also go for the IPv6 address or the domain name. The Endpoint port is51820
. Under Instances select «Mullvad» or whatever name you picked in step 3. I also like to set the Keepalive interval to25
. Also ensure that Enabled is checked, it should be by default. Hit Save then Apply.You can now go to VPN > WireGuard > Status and verify that the tunnel is up. It should show the Handshake Age and some bytes under the Sent and Received columns.
NAT and routing
The tunnel to Mullvad is up. Next we need to route all traffic through the tunnel. This involves first setting up Network Address Translation and then setting up the routes for IPv4 and IPv6 separately.
Go to Firewall > NAT > Outbound and select Hybrid outbound NAT rule generation or Manual outbound NAT rule generation. Choose Hybrid to keep automatic NAT rules while adding custom rules, or Manual for complete control over all NAT rules. Then hit Save.
Hit the plus sign. The Interface to select is
VPN
, the TCP/IP Version to select is IPv4. Set Protocol to any, the Source address to INT net, the Destination address to any, the Translation / target to VPN address. Leave the remaining settings on default. Hit Save then Apply changes. Repeat for IPv6, where you set the TCP/IP Version to IPv6 instead of IPv4.Go to Firewall > Rules > INT and hit the plus sign. Leave everything on default but set the Source to INT net and the Gateway to Mullvad4 - 10.64.0.1. Hit Save then Apply. Repeat for IPv6, where you set the TCP/IP Version to IPv6 and the Gateway to Mullvad6 - fc00:bbbb:bbbb:bb01::1.
For completeness sake here are the remaining important default values: Check Apply the action immediately on match under Quick, select in as the direction and any as the Destination.
If you prefer to manually assign IP addresses and DNS servers to your clients on the INT
subnet, you can finish here. The next section configures DHCP for IPv4 and IPv6 with Router Advertisements and automatic assignments of DNS servers.
Setting up DNS and DHCP
I like to automatically assign addresses and DNS servers to the clients on INT
via DHCP, so that I do not have to do any configuration on the clients. A few popular choices are AdGuard DNS, Cloudflare, dns0.eu, DNS4EU, Google, Quad9 and UncensoredDNS.
I may have overlooked something, but Dnsmasq and Kea didn't work for me, as I didn't figure out how to configure the DNS servers for the clients. So I decided to use the ISC DHCP server.
Go to Services > Dnsmasq DNS & DHCP > General and uncheck Enable.
Go to Services > ISC DHCPv4 > [INT] and check Enable DHCP server on the INT interface. As Range select some range from your
INT
interface that you feel is suitable. For example, if10.0.0.0/24
is your network, you may want to pick10.0.0.100
to10.0.0.254
.Under DNS servers enter the appropriate IPv4 DNS servers. Hit Save.
Go to Services > ISC DHCPv6 > [INT] and check Enable DHCPv6 server on INT interface. As Range select some subnet from your
INT
interface that you feel is suitable. For example, iffd00::/64
is your network, you may want to pickfd00::100
tofd00::ffff
.Under DNS servers enter the appropriate IPv6 DNS servers. Hit Save.
Go to Services > Router Advertisements > [INT] and select Stateless as Router Advertisements. Also check Use the DNS configuration of the DHCPv6 server under DNS options. Hit Save.
Adding a kill switch
A kill switch prevents traffic from the INT
subnet from leaking through the WAN
interface if the VPN connection drops. This ensures that client devices cannot bypass the VPN accidentally.
Go to Firewall > Rules > Floating and hit the plus sign. Select Block as Action, ensure that Apply the action immediately on match under Quick is checked. As Interface select WAN
, under TCP/IP Version select IPv4+IPv6. As Source select INT net and leave Destination on any. Hit Save then Apply changes.
With this rule in place, any traffic from INT
attempting to exit through WAN
will be blocked, ensuring that clients can only communicate when the VPN is active.
Testing the setup
From the command line on one of the clients in the INT
subnet, I like to use curl
against IPinfo to test the IPv4 connectivity:
{
}
We can also test for IPv6 connectivity:
{
}
Both responses should show the Mullvad server's location (in this case, Helsinki) rather than your actual location, confirming that traffic is properly routed through the VPN.
I also like to check web-based tools like Mullvad's Connection Check, AirVPN's IP Leak, BrowserLeaks or IVPN's DNS Leak Test. In my case, all come back with the IP addresses and DNS servers that I configured.
Troubleshooting
This section covers common issues you might encounter during setup and performance optimizations for production deployments.
Disable DAD for SR-IOV
In environments using SR-IOV virtual functions, IPv6 Duplicate Address Detection (DAD) can sometimes cause connectivity issues. This is particularly common when passing SR-IOV virtual functions to virtualized OPNsense instances.
In my home lab setup with a Mellanox ConnectX-4 Lx and SR-IOV enabled, I encountered DAD conflicts when using a virtual function as the WAN
interface. The solution was to disable DAD:
Go to System > Settings > Tunables and hit the plus sign. As Tunable enter net.inet6.ip6.dad_count
and as Value enter 0
. Hit Save then Apply.
This issue typically occurs because the hypervisor and virtual function may both attempt to use the same IPv6 addresses, triggering DAD failures. For home lab environments where address conflicts are unlikely, disabling DAD provides a simple solution. In production environments, consider per-interface IPv6 configuration or VLAN isolation as alternatives.
If you're not using SR-IOV or virtualization, you can skip this configuration.
Maximum Transmission Unit (MTU) and Maximum Segment Size (MSS)
WireGuard adds different overhead depending on the IP version: 60 bytes for IPv4 transport and 80 bytes for IPv6 transport. Since this is a dual-stack setup, we use the larger IPv6 overhead value to accommodate both protocols.
Configure the following settings:
- For the
VPN
interface under Interfaces > [VPN], set MTU to1420
(standard 1500 MTU minus 80 bytes WireGuard overhead for IPv6). - For the
INT
interface under Interfaces > [INT], set MSS to1420
. OPNsense will automatically subtract TCP/IP headers (40 bytes for IPv4, 60 bytes for IPv6) to clamp MSS correctly.
These values ensure that packets don't exceed the tunnel capacity and avoid fragmentation, which can significantly impact performance. Different network environments may require fine-tuning these values based on your upstream MTU constraints.
For more details about MTU and its impact on network performance, see Cloudflare's MTU explanation.
Note: You might need to test different MTU values if you experience connectivity issues or poor performance, as some ISPs or network paths have smaller MTU limits.