This post is about my experience running a Linux desktop virtual machine on a Linux host. I used the VM interactively, often in fullscreen mode, to develop software and run web apps (mostly Slack and Google Docs). My experience wasn't great, as I ran into many challenges.
My previous post on running VMs discussed running bare-bones Linux VMs and getting SSH access to them. I usually do that for testing, where the VMs are light-weight, shortlived, and disposable. It turns out that long-lived desktop VMs have more challenging requirements and come with a whole new set of issues.
VM setup
Following a virt-install
, I did many of the tasks I normally do for setting
up a Linux host (see my configs repo),
including setting the hostname, setting the timezone, configuring APT,
installing packages, and configuring other software.
I renamed the username and group from debian
to diego
, while logged in as
root
:
usermod -d /home/diego -m debian
usermod -c 'Diego' -l diego debian
groupmod -n diego debian
I could have created a new user instead, but I guess I like having uid
1000.
CPU and RAM
For CPU, I typically assigned 4 vCPUs to the VM. Changing this value requires restarting the VM. Just because the VM has a vCPU doesn't mean it's using it, so it might be reasonable to set this to half or more of the host CPUs. I think 4 vCPUs is the bare minimum needed for software development with VS Code and Rust, and I probably should have allocated more.
For RAM, virt-manager
has a field for the maximum allocation and another for
the current allocation. Changing the maximum allocation requires restarting the
VM, but changing the current allocation up to the maximum can happen at
runtime.
I created an additional swap disk to relieve memory pressure. Using a separate
disk image was convenient because I didn't have to copy it every time when
moving the VM to a different host. I still copied the swap image the first time
because the VM's /etc/fstab
referred to the swap partition's PARTUUID
.
I also set
browser.tabs.unloadOnLowMemory
to true
in Firefox, which may help with memory pressure.
Display
For video settings, I tried a few different options. My main problem was that I
encountered serious rendering glitches with Google Docs in Firefox. I
ultimately used Virtio
with 3D Acceleration
on and a display type of Spice Server
with OpenGL
on. However, I set gfx.canvas.accelerated
to false
in
Firefox to work around the Google Docs rendering glitches.
I wanted the VM to be able to run in both windowed and fullscreen modes,
which for my widescreen monitor meant 1792x1344
and 5120x1440
resolutions.
The default display resolutions were missing 5120x1440
. I created
/etc/X11/xorg.conf.d/10-monitor.conf
:
Section "Monitor"
Identifier "Virtual-1"
Modeline "5120x1440_60.00" 624.50 5120 5496 6048 6976 1440 1443 1453 1493 -hsync +vsync
Option "PreferredMode" "1792x1344"
EndSection
I got that Modeline
by running:
$ sudo apt install xcvt
[...]
$ cvt 5120 1440
# 5120x1440 59.96 Hz (CVT) hsync: 89.52 kHz; pclk: 624.50 MHz
Modeline "5120x1440_60.00" 624.50 5120 5496 6048 6976 1440 1443 1453 1493 -hsync +vsync
I found that the Xfce Display Settings app was causing the VM to enter
5120x1440
mode on login, even though I wanted it to default to 1792x1344
. I
think I removed .config/xfce4/xfconf/xfce-perchannel-xml/displays.xml
and
then never opened the Xfce Display Settings again to work around that.
I created two scripts and two launcher buttons on the Xfce panel to switch
between windowed and fullscreen modes. The video-windowed
script:
#!/bin/sh
exec xrandr --output Virtual-1 --mode 1792x1344
And the video-fullscreen
script:
#!/bin/sh
exec xrandr --output Virtual-1 --mode 5120x1440_60.00
Finally, I ran into an issue with the
Notion window manager when running the VM in windowed
mode. I normally hold the Meta key and drag the right mouse button to resize
windows. This doesn't work. I could resize a window by dragging the left mouse
button on the window border, but that border so thin that it's hard to hit. As
a workaround, I enabled Shift+Meta
while dragging windows in the VM, in
.notion/cfg_bindings.lua
:
bdoc("Resize the frame."),
mdrag("Button1@border", "WFrame.p_resize(_)"),
mdrag(META.."Button3", "WFrame.p_resize(_)"),
+ mdrag("Shift+"..META.."Button3", "WFrame.p_resize(_)"),
Suspend
I wanted to be able to keep this VM "turned on" even while the host computer was suspended or hibernated.
Early on, I found that running suspend or hibernate within the VM did not work, and I disabled it:
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
The Xfce logout dialog stops showing the buttons to take these disabled actions, which is nice.
I also found that I couldn't pause and resume the VM in virt-manager
successfully.
I could generally suspend the host, and the VM would tolerate that.
However, in March 2025, the VM kept crashing when the host suspended. I
upgraded the qemu-*
and seabios
packages to the Debian 12 backports
versions (and had to install qemu-system-modules-spice
), which resolved the
suspend issue.
Disk image size and VM migration
I occasionally moved this VM from a desktop to a laptop and vice versa. I didn't set up any fancy online migration; I just shut it down and copied the disk image.
Ideally, I'd free up some space and shrink the disk image prior to copying it.
I have discard
enabled in the VM's /etc/fstab
. I found I had to change the
virt-manager
disk's Discard mode
setting from Hypervisor default
to
unmap
. Then I ran:
sudo fstrim -v /
within the VM, which reported that it trimmed many gigabytes. The qcow2
image
shrank in size accordingly on the host. The manual fstrim
may not be
necessary if you configure virt-manager
from the start.
I used rsync
to copy the disk image, with the --compress
and --sparse
flags. I probably should have used the --inplace
flag also (which used to
conflict with --sparse
but doesn't anymore).
To copy the VM's configuration, I ran this on the original host:
virsh dumpxml $NAME > $NAME.xml
And then imported it on the new host:
virsh define --file $NAME.xml
I also needed to change the display Splice device to auto
, but that might be
because I had messed with it on the original host.
Keyboard
I set Caps Lock to be Control on some keyboards. I found I had to do this again within the VM by setting:
XKBOPTIONS="ctrl:nocaps"
in /etc/default/keyboard
.
Webcam
I tried passing through a USB webcam for video conferencing, but this was too slow to work reliably. I don't have a good solution to this. My workaround was to run the video conferencing on the host.
Conclusion
That's it. I used this VM for months, so I don't think I'd find new issues beyond these. The good news is that most of these are either easy to work around or acceptable limitations, with some patience. My top remaining issues are:
- No webcam support,
- No ability to suspend/hibernate/pause the VM, and
- No dynamic CPU allocation.
Webcam support is important, especially because video conferencing often needs to be authenticated, and that account may "belong" inside the VM. I don't know how to solve it today, other than perhaps passing a PCIe USB card through to the VM. The last two are "software issues" and might well be solvable with some more configuration effort.