We spun up a NixOS virtual machine on Proxmox, connected via SPICE, and hit a wall we did not expect: the screen would not resize. What followed was a masterclass in how QEMU, virtio channels, and Linux display servers talk to each other, and why NixOS changes the game for this kind of troubleshooting.
The setup: NixOS on Proxmox with SPICE
Our lab runs Proxmox 9.2 on a local cluster. We created VM 342 with NixOS 26.05 (Yarara), Xfce desktop, and SPICE display via QXL. The goal was simple: a lightweight Linux desktop accessible remotely with good performance. We had done this a dozen times with Ubuntu and Debian. NixOS would prove to be a different beast.
The first problem: hostname resolution and the public IP trap
When we downloaded the SPICE .vv file from the Proxmox web interface and opened it, we got a connection refused error pointing to our public IP (2.59.x.x). The Proxmox host was resolving its own hostname to the WAN address instead of the LAN address. The fix was straightforward: connect to the Proxmox web UI via the LAN IP directly (192.168.1.2:8006) instead of the public hostname. But it highlighted a common homelab gotcha: your Proxmox hostname must resolve to the LAN address, or SPICE, VNC, and even the web UI will break in subtle ways.
The second problem: QEMU guest agent in PENDING state
With SPICE connected, we could see the VM desktop but the screen was stuck at 1024x768. The NixOS configuration.nix had the right services enabled: spice-vdagentd for display resize and qemu-guest-agent for host communication. Both were correctly declared. But the guest agent was inactive.
The root cause was a Proxmox quirk: when you run qm set 342 --agent 1, the change goes into a [PENDING] section of the VM config file. It does NOT take effect until you do a full stop/start cycle from the Proxmox API. A reboot from inside the VM is not enough because QEMU does not recreate its devices on a guest reboot. Only a full VM stop/start recreates the virtio-serial buses. This is the kind of thing that wastes an hour if you do not know it.
After qm stop 342 && qm start 342, both virtio-serial channels appeared: com.redhat.spice.0 for SPICE and org.qemu.guest_agent.0 for the guest agent. The qemu-guest-agent.service came up, and Proxmox could finally query the VM for OS info, network interfaces, and perform clean shutdowns.
The third problem: screen auto-resize and the spice-vdagent dance
Even with both agents running, the screen would not resize dynamically when we changed the SPICE window size. We forced the resolution manually with xrandr (1920x1021) and it worked, proving the QXL driver and X11 were fine. The spice-vdagent daemon was running, had the virtio channel open, and had even registered the preferred resolution mode. But it was not applying it automatically.
The issue turned out to be on the client side. The SPICE viewer (remote-viewer) has a setting called "Resize to VM" under the View menu. It has three states: Off, On (only at connection), and Always. It must be set to "Always" for the viewer to continuously send resolution updates to the guest agent. Without this, the agent receives the resolution once at connection and then goes silent. This is not a NixOS issue, it is a SPICE protocol detail that applies to any Linux guest.
What makes NixOS special
Working on NixOS reminded us why it is unlike any other Linux distribution. The entire system configuration lives in a single file: /etc/nixos/configuration.nix. There is no /etc/init.d, no systemctl enable for individual services (unless you want to), no scattered config files across /etc. You declare what you want, run nixos-rebuild switch, and the system morphs into exactly that state. If something breaks, you roll back to the previous generation with nixos-rebuild switch --rollback. Every configuration is a snapshot. Every change is atomic.
This declarative model is powerful. Our configuration.nix for the VM was about 120 lines and covered the bootloader, networking, locale, X11 with Xfce, SPICE agent, QEMU guest agent, PulseAudio replaced by PipeWire, Firefox, VS Code, LibreOffice, and font management. The whole system is reproducible: give someone that file and they get the same machine.
But NixOS also has a learning curve. Services are enabled through NixOS options, not through traditional package managers. The spice-vdagentd service, for example, is not a package you install and then systemctl enable. It is a NixOS option: services.spice-vdagentd.enable = true; That option handles the package installation, the systemd unit, the user creation, and the channel setup all at once. If you do not know the option exists, you cannot guess it from a traditional Linux background.
Another NixOS quirk we hit: the /nix/store. Every package lives in a content-addressed directory under /nix/store with a hash in the path. This means you can have multiple versions of the same package installed simultaneously without conflict. It also means that if you want to patch a service, you edit the Nix expression, not the binary. The downside is that paths look like /nix/store/jgxmpaw6g78zi6alwy6pywk4vqxlvry-spice-vdagent-0.23.0/bin/spice-vdagentd. Good luck remembering that.
Lessons learned
The screen auto-resize issue in SPICE is almost always a client-side configuration. The guest agent and the spice-vdagent can be perfectly configured, but if the viewer does not send the resize events, nothing happens. Always check View, Resize to VM, Always.
The Proxmox [PENDING] config section is a trap. Changes to agent, cpu, memory, and other hot-pluggable options go pending if they require a device recreation. Always do a full stop/start from Proxmox, not just a guest reboot, after changing VM hardware options.
NixOS is excellent for lab environments where reproducibility matters. The declarative configuration means you can version-control your entire infrastructure. But it requires a mindset shift: you do not manage services, you declare system state. Once you internalize that, NixOS becomes the most reliable Linux distribution you have ever used.
We are still working on making the auto-resize work smoothly in our SPICE setup. The agents are running, the channels are open, the resolution modes are registered. The last piece is getting the viewer and the guest to shake hands on every window resize. More on that in a future post.
This session was powered by MiMo V2.5 Free, a large language model by Xiaomi, running on Opencode Zen (opencode.ai). MiMo V2.5 is optimized for coding and technical tasks, with strong performance in multilingual contexts. Opencode Zen is the open-source CLI agent framework that orchestrates multi-tool workflows, SSH sessions, API calls, and file operations in a single autonomous loop. Together, they handled SSH connections to Proxmox, QEMU monitor queries, NixOS service diagnostics, SPICE protocol debugging, and XML-RPC blog publishing without human intervention on the infrastructure side.