Changing Default MTU for Networking VMs

My network requires a MTU of less than 1500 in order to have a good speed. Every time I restart Qubes, I have a networking VM and a firewall VM that have a MTU of 1500 and I have to manually change this. This is also difficult because there are multiple "vif"s that I need to change which are generated with different semi-random names with every restart (and sometimes even after a restart, after the VMs have booted, if a new connection is made to the VM).

I am also using Debian instead of Fedora for my networking VM and firewall VM because I am more comfortable with Debian than Fedora, but this may be a mistake. I have heard Fedora has better mitigations against different kinds of attacks. I also think it would likely be safer to use multiple OS systems for my firewall and network VM to make an attack harder (since breaking in would likely need to involve multiple types of exploits in 2 VMs).

This is Qubes specific because only in Qubes will I get vif5.0, vif6.0, vif108.0, vif120.0, etc and need to change them in multiple Qubes and this involves multiple OSes. Due to the fact that the vif numbers vary (vifX.0 where X is a semi-random number), I do not know the best way to approach this and manually changing things each time is annoying. I also am not sure whether it is more secure to use Debian or Fedora and whether a minimal template is safer.

I also have the same problem in my whonix VM, with random vif interfaces showing up and needing to manually change the MTU to get a faster and more reliable connection.

I also don’t know whether I should be trying to change this directly in the templates and whether this will be overwritten by upgrades. Any guidance would help because I don’t conceptually understand the best approach to this, and to clarify, it would be incredibly unhelpful to get a response like, “This is not Qubes specific! Go ask in the Debian, Fedora, and Whonix forums!”

Is your sys-net disposable? If not you can set the MTU from the NetworkManager settings for your connection. Otherwise you could place a line of code (see nmcli) in /rw/config/rc.local of the DispVM template used to create sys-net. If that doesn’t work you can try placing a script in /home/user/.config/autostart in the same DispVM template.

Otherwise you might set the default config in /etc/NetworkManager/ of the TemplateVM (you’ll have to do a bit of research on this, I don’t know the exact parameters).

An attack on Fedora will likely be incredibly similar (if not the same) to one on Debian. Instead, you may want to consider using an entirely different vm for your firewall, such as Mirage: GitHub - mirage/qubes-mirage-firewall: A Mirage firewall VM for QubesOS (this one specifically uses 32MB of RAM, so another plus).

Debian vs Fedora: you make your own judgment here, with Debian you have the option of further hardening via distro-morphing to Kicksecure:

Minimal templates: less programs installed == less running code == smaller attack surface (and ram usage). Win win.

I use both: win win, win.

Generally speaking, if you want something to be persistent across multiple vms, do it in a TemplateVM. Changes to AppVMs (or DispVM templates, in case of DispVMs) which don’t reside in /home can be made in /rw (see for example the /rw/config/rc.local script).


Using Network Manager doesn’t work. I can set the MTU for the adapter connecting to the source when I open the network manager gui, but I can’t set the MTU for the vif connects that appear with random names. I will have 1 connection to something (let’s call it WiFi0) and I can set the MTU for that and make it 1000, but it will not change that all the vifs that are created after that will be 1500. It’s a real pain if there are multiple connections to the firewall that have new random #s after the vif part.

Unfortunately, these are not disposable. I think I tried to create disposable networking VMs and they wouldn’t connect correctly. I also understand how to modify MTU with ifconfig in Debian, I don’t know how to do this correctly in Fedora. (That is probably something I can figure out somewhat easily, I just wanted a quick work around and so am using Debian for now.)

Do I modify those files in both Fedora or Debian? Will those file locations be the same?

I am not sure how to even look up what code to write in those files.

The reason I think this is Qubes specific is that I’m not sure of an other OS where vifs are generated with random #s after them ("vif33.0 vif34.0 etc) and it’s hard to predict what they will be.

I am not sure where I can look for what to write in these files since the code would need to be so Qubes specific due to the vif naming seeming to be unique to Qubes, unless I am wrong.

Alright, try this in your TemplateVM that is used for sys-net (you’ll need root privileges):

Note: the script assumes that all VIFs follow the same naming pattern: vif#.# (doesn’t matter what # as long as it’s a number)

  1. Copy the following script to /usr/bin/ (change the mtu value according to your needs, here it’s 1500):
for vif in $(ip link show | grep -o 'vif[0-9]*\.[0-9]*'); do
    ip link set $vif mtu 1500
  1. Make it executable:
chmod +x /usr/bin/
  1. Copy the following to /etc/systemd/system/set_mtu.service
Description=Set MTU


  1. Reload and enable:
systemctl daemon-reload
systemctl enable set_mtu.service
  1. Shutdown your template, restart sys-net and check if the service is running:
systemctl status set_mtu.service
1 Like

This is the original poster. I forgot my password. It was something easy, but I have no idea what it is.

I tried your sh script and it doesn’t resolve the issue.

I cloned Debian 11 to Debian11networkmod and then opened it, went into the terminal, went into root, then created the files using nano and just Ctrl C, Ctrl Shift C, Ctrl Shift V, right click paste. I didn’t change anything you wrote.

I did it in the order you said. Shut it down, restarted it, changed the template for system-networking and system-firewall to Debian11networkmod and the problem still persisted.

Since I was getting vif25.0 showing up, I thought that perhaps the problem was that it says 'vif[0-9] in one part, so I changed that to vif[0-900] and it didn’t change anything after restarting everything.

I think one part of the problem is that these vif connections do not show up immediately when I first run these templates via the networking qubes. They show up after other qubes attach to them, and anytime a qubes has the firewall as it’s network source, I get a new vif. I think the script you created is looking at things during boot and modifying them, but at boot there is nothing to modify. However, this might not be the issue at all.

I really need a script that runs anytime a new vif is created to modify it, and I am not sure how to do that.

Is it possible this shouldn’t be done in the TemplateVM and needs to be done in the AppVM?

Please see the above post, I am the original poster but forgot my password, I am adding this as a reply so you see it.

  1. shut down the template
  2. shut down sys-net
  3. change the template of sys-net
  4. start sys-net

No you can reverse it, the script already does that.

Then shut down sys-net, and once it’s down start a browsing vm. That’ll auto-start sys-net and the vif should be created right away. Check if the MTU is changed.

Doesn’t matter where it is, the template makes it persistent. In the appvm you’d have to use a persistent folder like /home or /rw or resort to bind-dirs.

Make a cron job. You already have the script I made you.

What’s the output of systemctl status set_mtu.service ?

1 Like

I had a similar MTU issue with Wireguard while traveling (qubes MTU were too high). I’m sure you can reuse the following iptables rule (iptables -t nat -I POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu) to force your qubes using the ProxyVM to use the specified MTU.
First, try to apply this rule in the ProxyVM and play around to see if the MTU is used by all qubes attached to it ( can tell your current MTU). Then, you can make it persistent using the proposed solutions in this thread. If your sys-net/sys-firewall is disposable and you just want to test things or don’t want to put too much efforts, you can create a new AppVM, set its network to sys-net/sys-firewall and attach all qubes you want on it instead. If you do that, you’ll have to apply that iptables rule on the new AppVM and do the persistence on it (/rw/config/rc.local for example).

1 Like

Qubes provides just such a mechanism, using executable scripts in
It’s covered (with an example) in the docs
Don’t do anything more complicated unless you have requirements that go
beyond what Qubes provides.

You set the mtu with (e.g)
ip link set dev FOO mtu NUM

and call that from your networking script - the name of the vif (FOO
above) is passed in as an argument when the interface comes up.

On a side note, you’ll find that ifconfig is not available universally
whereas ip is, and you should use it. (Same for iptables and nft )

I did this before and it wasn’t changed. I only changed it to 0-900 after it didn’t work at all.

This is the original poster. I have lost my password again.

In the template:

systemctl status set_mtu.service
â—Ź set_mtu.service - Set MTU
Loaded: loaded (/etc/systemd/system/set_mtu.service; enabled; vendor prese>
Active: inactive (dead) since Sun 2023-02-19 10:51:22 EST; 1min 21s ago
Process: 433 ExecStart=/usr/bin/ (code=exited, status=0/SUCCESS)
Main PID: 433 (code=exited, status=0/SUCCESS)
CPU: 6ms

Warning: some journal files were not opened due to insufficient permissions.
lines 1-8/8 (END)

In the system-network:

systemctl status set_mtu.service
â—Ź set_mtu.service - Set MTU
Loaded: loaded (/etc/systemd/system/set_mtu.service; enabled; vendor prese>
Active: inactive (dead) since Sun 2023-02-19 10:51:22 EST; 1min 21s ago
Process: 433 ExecStart=/usr/bin/ (code=exited, status=0/SUCCESS)
Main PID: 433 (code=exited, status=0/SUCCESS)
CPU: 6ms

Warning: some journal files were not opened due to insufficient permissions.

It says it is inactive, I think this is what is causing the issue. I changed it back to 0-9. It doesn’t work no matter what I do.

I am not a network engineer or even a long time linux user. I am not knowledgable with IP tables. Please do not suggest I download Puppy Linux and to stop using Qubes!

I can copy that, but don’t know how this would apply. I use different qubes for different types of connections attached to my firewall. This is one of the ways Qubes makes working so much easier. I wouldn’t know how to use this. Are you saying I would need to run this in all of them? Do I just copy and paste that into them all and do I have to do it every time it runs?

I did see ifconfig was not universally available. I didn’t know I could use ip instead.

Are you indicating that I should just edit that file (like with sudo nano /rw/config/network-hooks.d") and then add:

“ip link set dev FOO mtu 1000” if I want them all to have 1000 as the mtu?

If anyone else reads this, can they help me understand how to implement unman’s post? Do I need to take the script from @BEBF738VD and modify it?

You only paste it in the ProxyVM (the one used by all your qubes for networking access).

1 Like

Did you read the link that I posted?
The one where I told you there was a worked example?
If you did you would realise that /rw/config/network-hooks.d is a
directory and you write files in that directory.

You can look at the script that is on that page and adapt it under a new
You don’t need the if statement, because you want the change to happen to
all qubes, whereas that example only applies to the work qube.
You don’t need anything for offline, when the vif goes down, because the
vif interface disappears when the qube stops.

So your updated file could look like this:



case "$command" in
    ip link set dev $2 mtu 1000

notice that we dont actually do anything with vif_type, and ip, in this


I did read that page, but don’t have all the knowledge you have, nor the aptitude, although I still want to improve. In the world of computers and code, I am less than a grain of rice and you are Mount Kilimanjaro.

Unfortunately, I tried to do what you said and it isn’t working.

I tried to create a script using sudo nano /rw/config/network-hooks.d/ and copied your script, then tried to save it. nano said the directory didn’t exist.

I ran sudo nautilus and created the directory, then went back to nano and created the file.

i went back into sudo nautilus and made the file executable. I realize I could have done this by CLI instead.

I restarted everything and nothing had changed.

I took the file in /rw/config/network-hooks.d/ and copied it to /rw/config/rc.local/ and am about to restart again. The file is still executable. I am going to restart everything and post the outcome.

The files I am modifying are in the AppVM, not the Template.



1 Like

If I put this in the template, will it mean that all AppVMs based on the template will have the lower MTU? Because I never want the default 1500. Could I just modify the Debian template and the Fedora template and have this any template based on it? I have more than a few AppVMs.

Congratulations on getting this working.
Unfortunately qubes don’t inherit anything in /rw/config from their
templates, as explained here
You’ll need another approach.

Hello @unman this is the original poster.

I rebooted and today realized this doesn’t work at all.

It’s only changing the MTU for the first network device (like eth0). When I saw it changed the first device, I was so happy I didn’t do any more testing.

It doesn’t change any of the vif3.0 or vif10.0 etc. I put the scripts in those directories, I rebooted, it just won’t change them. Everything is at 1500.

I am sorry to ask for help again on this, but I have no idea how to approach this problem. My coding knowledge is very low, I don’t think I can create my own script to solve this problem.

I’m probably doing something wrong, but I don’t know what or how to approach this.

Do I have to use systemctl to enable something?

$ iptables -t nat -I POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
bash: iptables: command not found

this is not resolved. i was incorrect. please read other responses

I can’t reply to you @DVM so I am hoping you will see this new mention. It says I am too new and am spamming the forum.

I ran the command with sudo. Something happened, but I don’t know what. I’ll test it now. Is it getting the MTU now from the network manager? I’m not seeing an MTU in that command.

In order to test this command, I am going to have the shut down my system-firewall and reload it. When I do that, am I going to have to run the command again? Is there a way to make the command automatically load? Where is the command getting the MTU from? Is there a way to make the MTU that it is retrieving be a certain value (like 1000) at boot?

@DVM edit again to respond to your post, since the forum software is blocking me from replying for 7 hours. Browser leaks usually shows my MTU as near what I set it to, but slightly less than that. never seems like a good indication of the MTU. Instead, I just use sudo ifconfig and read the output of the interfaces. Is this not correct?

@DVM edit again. I don’t understand what you wrote. ( I guess your sys-firewall is disposable, so you’ll lose everything each reboot. You can put a new AppVM in front of the firewall, put that iptables rule in /rw/config/rc.local and set the other VMs to use it instead of sys-firewall. That’s the easiest workaround.) My system-firewall is not disposable, although at some point I want it to be. Will the command persist even after I shut it down? I know when I restart the system-firewall VM, I lose all of the settings for the interfaces and get new vif#.#s every time I open it.

Would I just put this command as a script in rc.local? Like create and put the command in there and make it executable?