[Tutorial 4.2&4.1] Mullvad Wireguard with Qubes

I am posting instructions for installing Mullvad VPN as a wireguaofficial instructions

The following tutorial is tested and is mainly an update to the official instructions for creating a VPN.
The instructions have been revised and the whole thing has been successfully tested.

  1. Create a new qube:*

Click on the Qubes app menu and then Create Qubes VM.

  1. Name and label: MullvadVPN.
  2. Type: Qube based on a template (AppVM).
  3. Template: fedora-38 (or later).
  4. Networking: default (sys-firewall).
  5. Advanced: Check (enable) provides network.
  6. Click on OK.
  1. (example: in personal qube) Visit www Log in | Mullvad VPN
  2. check the option “Linux”:


  1. The server that our computer connects to (this is the server that the local ISP sees). This means that in this example, my computer will only connect to the Honk Kong server, while traffic from the Honk Kong server will be redirected to the Israeli server.
    4.That is, I am connecting to Hong Kong, while the outgoing IP address is Israeli.*

4.Check the “kill switch” option

  1. (Optional) select the content to which mullvad should block access:
  2. Generate and download configuration.

Note: In the Qubes app menu, the newly created MullvadVPN AppVM qube will show up as “Service: MullvadVPN” and not “Qube: MullvadVPN” due to its “provides network” setting.

  • Click on the Qubes app menu and go to your current AppVM and open Files.
  • Open the Downloads folder and right click on the downloaded WireGuard file.
  • Select Copy To Other AppVM… and then enter MullvadVPN as the Target and click on OK.

8. Install WireGuard

We will install WireGuard in the Fedora-38 template so your MullvadVPN ProxyVM can use that.

  1. Click on the Qubes app menu and go to Template: fedora-38 and open the Terminal.
  2. In the Terminal run the command
    sudo dnf install wireguard-tools -y
  3. Shut down the VM with the command
    sudo shutdown -h now
  4. Shutdown and Start the MullvadVPN so that wireguard-tools are added to it.

Configure WireGuard

In the MullvadVPN VM:

  1. Click on the Qubes app menu and go to MullvadVPN and open the Terminal.
  2. Now you will copy the WireGuard .conf file that was copied from the other AppVM to the /home/user/ folder so it persist after reboot. First run:
    cp /home/user/QubesIncoming/*/se9-wireguard.conf /home/user/
  3. Now you will edit the /rw/config/rc.local file using a text editor. First install nano:
    sudo dnf install nano -y
  4. Then run
    sudo nano /rw/config/rc.local
  5. Add
    wg-quick up /home/user/se9-wireguard.conf
    (or the config file you used) on a new line.
  6. Press Ctrl+O (Enter) and then Ctrl+X to save and exit.

Make sure that WireGuard connects:

  1. Run
    sudo wg-quick up /home/user/se9-wireguard.conf
  2. Run
    curl https://am.i.mullvad.net/connected
  3. Run
    sudo wg
    and check for a WireGuard network interface and a peer handshake.

Add DNS hijacking rules

Now we will add firewall rules to redirect DNS requests to (the DNS on the VPN server) for all AppVMs that use the MullvadVPN ProxyVM.

Make sure that you have started an AppVM that has the Networking set to MullvadVPN, otherwise the “vif” IP address will not be visible.

Still in the MullvadVPN Terminal:

  1. To find out your MullvadVPN_local_IP address, run
    ip a | grep eth0
  2. Edit the firewall user file with nano:
    sudo nano /rw/config/qubes-firewall-user-script
  3. Copy and paste the following in the bottom.
    4.Only Replace*
    with your own MullvadVPN_local_IP IP address:
**# replace with the IP address of your vif* interface**
iptables -F OUTPUT
iptables -I FORWARD -o eth0 -j DROP
iptables -I FORWARD -i eth0 -j DROP
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS  --clamp-mss-to-pmtu
iptables -F PR-QBS -t nat
iptables -A PR-QBS -t nat -d $virtualif -p udp --dport 53 -j DNAT --to $vpndns1
iptables -A PR-QBS -t nat -d $virtualif -p tcp --dport 53 -j DNAT --to $vpndns1
  1. Press Ctrl+O (Enter) and then Ctrl+X to save and exit.

Set the Networking

  1. Click on the Qubes app menu and go to System Tools > Qube Manager.
  2. Select the AppVM that you want to use with the MullvadVPN ProxyVM and click on the Stop button in the toolbar to shut it down.
  3. Right click on the same AppVM and then select Qube settings.
  4. On the Basic tab, click on the Networking drop-down list and select MullvadVPN.
  5. Click on OK.
  6. Click on the Start button in the toolbar to start the AppVM again.

Kill-switch works only when a Wireguard connection is established.
When VM:Mullvad VPN is started before Wifi traffic is established, wireguard will not be automatically set up.
The solution is to create a VPN setup button in the Dom0 panel.

  1. Point the mouse cursor at the Dom0 panel, then press the right mouse button and select the option
    panel → panel preferences
  2. in the “Items” tab, press the plus icon, and then press twice on the “launcher” item
  3. in the launcher settings, place the pointer in the “Command” field and enter the following command:
qvm-run --pass-io MullvadVPN 'sudo wg-quick up /home/user/config.conf'

Remember to replace the name of the “config.conf” file with the name of the wireguard configuration file downloaded from the mullvad website.

Check the option “Run in terminal” and press “save”. From now on, every time you press the icon, it will trigger an attempt to connect to the tunnel.

Preventing leaks in the browser

  1. Run the firefox browser on the AppVM connected to the MullvadVPN network
  2. Go to the website Download Extension |Mullvad VPN
  3. press the “Install” button, and then accept the request to install the browser add-on.
  4. when you see the mullvad icon on the firefox icon bar, then press on it and select “Connect Proxy” option.
  5. the Proxy will only work if the Mullvad tunnel is running. When the VPN connection is not established for some reason, the browser will stop allowing traffic to pass through to the Internet (that’s because the proxy is only reachable on the MullvadVPN network by the local mullvad server address)

Could you edit your post to change it to the category Community Guides ? :slight_smile: This will be easier to find it there in the future


Done! :slight_smile:


Are the iptables rules being actually applied? Starting with Qubes OS 4.2, a network VM comes with some nftables rules, which should make it incompatible with iptables commands :thinking:

The iptables rules still exist in the fedora-38 template.
commands responsible for the iptables function are running.

On the other hand, there is still no official position of Qubes on the matter, that iptables has been supplanted in any way.

That’s why I added a post at the end about firefox browser leak prevention, which works independently of iptables rules or “magic” nftables.

The wireguard tunnel is not broken when the sys-net is disconnected from the network.
The tunnel is still active.
In contrast, the Iptables rules in VM:Mullvad are activated only after the VPN wireguard tunnel is set up.
That’s why I think it’s important to create a shortcut button in Dom0 that sets up the VPN connection.
On the other hand, in the “mullvad” add-on in the firefox browser, you can check whether traffic is going out through the Mullvad link.

The kill-switch function is managed by wireguard.
I infer this from the fact that the iptables functions that act as “kill-switch” are in the Wireguard configuration file and are automatically applied when the command is turned on:
sudo wg-quick up /home/user/name_config file.conf

Wow, where can user read about it and even know it from in the first place?

I mean if it is a case, then it should be written in the changelog of R4.2 with capslock, because it will break user’s stuff if they use iptables in network qube.

1 Like


rc.local doesnt seem to run in 38-xfce, do i need to enable qubes feature or anything?

running sh /rw/config/rc.local manually as root starts the tunnel. i am confused

what do you have in mind?
Take a look at the snippet of my post:

This means that you need to add the command that starts the tunnel to the text file /rw/config/rc.local. The entry that starts the tunnel has the following syntax:
sudo wg-quick up /home/user/config.conf

After adding the above entry to the rc.local file, simply type the command in the console:
sh /rw/config/rc.local
instead of the command
sudo wg-quick up /home/user/config.conf

Let me explain this in another independent way.
After executing the command:
sh /rw/config/rc.local
the system will read the contents of the file and then try to execute the command.


  1. you type the command sh /rw/config/rc.local
  2. the system opens the file /rw/config/rc.local
  3. the system reads the command written in the file:
    sudo wg-quick up /home/user/config.conf
    and then executes it as root (root privilege is given by the sudo command)

yes its inside the rc.local file but when rebooting the vm the connection isnt up. as if the rc.local file wasnt executed. pushing it manually works. the file has he needed permissions, so now i wonder if i need to enable some service or qvm-features .

you could add something like this in your rc.local file: touch /tmp/boot_$(date +%s) to see if a file starting by boot_ is created in /tmp/ upon boot.

rc.local SHOULD just work all the time

1 Like

the /tmp/boot_ touch worked

here is journalctl:

Oct 01 21:47:24 wiredora misc-post.sh[1239]: wg-quick: `/home/user/config.conf' does not exist

maybe /home isnt mounted when rc.local is called? i tried to sleep 15 same result.


wg-quick up /rw/config/config.conf

worked… dont know what the vm hates about /home…

1 Like

I’m having the same issue on fedora xfce. I can execute sh rc.local after vm has started but it never runs automatically at start.

putting the config file in /rw/config does not solve the issue for me.

any Idea if there was something else you did to make it work?

Is this in 4.2 or 4.1?

I’m running 4.2 RC3 using fedora 38 xfce

Perhaps a clue is when I try to run this while in the working directory of the config file

wg-quick up config.conf

I get

wg-quick: `/etc/wireguard/config.conf.conf' does not exist

looks like an extra extension is added somewhere.

I get no such error when specifying the absolute path of the config file and wire guard start’s up fine then.

Ah, well, I don’t have 4.2 (yet) so I cannot try to reproduce the behavior.

Wireguard looks by default in /etc/wireguard, and if you run the command as you did, it expects you to specify an interface name, like wg-quick up wgvpn0 which will use a configuration file /etc/wireguard/wgvpn0.conf

If you specify a path, then the automatic append of “.conf” does not happen. This is explained in the manual page for wg-quick(8) .

Thanks for the explanation @barto. I’ll take a look at the man page.

I’m having the same problem on qubes 4.2 with the standard none xfce fedora 38 too.


I got it to work though I’m unsure of the security implications of the solution I found.

I found when I disable SELinux temporarily in rc.local I can run the wg-quick up command successfully. Then I can also enable SELinux afterword. I’ve added this to my rc.local

setenforce 0

wg-quick up /rw/config/config.conf

setenforce 1

Ugh… selinux is enforcing in 4.2! If you want to avoid the temporary removal of the enforcing selinux state (you don’t know what else is going on at the same time…) you can check the attributes of a file in /etc/wireguard and apply the same to your config file in /rw/something or /home/something.