- Shell 100%
Provision config files into /tmp and apply them via a new provision script (provision/scripts/15-system-config.sh). The script moves files into /etc and removes the temp copies. Also chown the /home/vagrant/.config directory to vagrant in bootstrap to fix ownership. |
||
|---|---|---|
| provision | ||
| .gitignore | ||
| inventory.ini | ||
| playbook.yml | ||
| README.md | ||
| run.sh | ||
| Vagrantfile | ||
Vagrant KVM/libvirt with XFCE Desktop
A complete setup for running a lightweight Debian 12 VM with an XFCE desktop on an exe.dev VM using Vagrant, KVM/libvirt, and SPICE.
Overview
This repo contains two things:
- Ansible playbook — provisions a bare Ubuntu host with KVM, libvirt, and Vagrant
- Vagrantfile — launches a Debian 12 guest VM with a full XFCE graphical desktop
You connect to the desktop using SPICE via remote-viewer, tunnelled over SSH.
Architecture
┌─ your laptop ──────────────────────────────────────────────┐
│ remote-viewer spice://localhost:5900 │
│ │ │
│ │ SSH tunnel (-L 5900:localhost:5900) │
└───────┼────────────────────────────────────────────────────┘
│
┌───────v─── exe.dev VM (Ubuntu 24.04) ──────────────────────┐
│ | KVM / libvirt / QEMU │
│ │ │
│ │ SPICE :5900 │
│ │ │
│ ┌────v─── guest VM (Debian 12) ────────────────────┐ │
│ │ XFCE 4.18 · LightDM (auto-login) │ │
│ │ spice-vdagent (clipboard, resize) │ │
│ │ 2 vCPUs · 2 GB RAM · QXL video │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
Prerequisites
On the exe.dev host
- Ubuntu 24.04 LTS
- Hardware virtualisation (
/dev/kvm) - sudo access
Run the Ansible playbook first (see Infrastructure Setup below).
On your laptop
- SSH access to your exe.dev VM
remote-viewer(part ofvirt-viewer):- macOS:
brew install virt-viewer - Ubuntu/Debian:
sudo apt install virt-viewer - Fedora:
sudo dnf install virt-viewer - Windows: download from virt-manager.org
- macOS:
Quick Start
1. Infrastructure Setup (one-time)
The Ansible playbook installs KVM, libvirt, QEMU, Vagrant, and the vagrant-libvirt plugin:
./run.sh
This will install Ansible automatically if needed. You can also run it manually:
ansible-playbook -i inventory.ini playbook.yml
Options:
./run.sh -e target_user=bob # install for a specific user
./run.sh -i my-hosts.ini # run against remote hosts
2. Launch the VM
sg libvirt -c "vagrant up"
Why
sg libvirt? Your shell session may not have thelibvirtgroup active yet (added by the playbook).sg libvirt -c "..."runs the command with that group. Alternatively, log out and back in.
The first vagrant up downloads the Debian 12 box and provisions XFCE. This takes a few minutes. After provisioning, reboot to start the graphical desktop:
sg libvirt -c "vagrant reload"
3. Connect to the Desktop
The exe.dev HTTPS proxy only handles HTTP traffic, so SPICE needs an SSH tunnel.
Step 1 — Open the tunnel (on your laptop):
ssh -L 5900:localhost:5900 vagrant-kvm-libvirt.exe.xyz
If you use a specific SSH key for exe.dev, add -i ~/.ssh/your_key, or configure ~/.ssh/config:
Host *.exe.xyz exe.dev
IdentitiesOnly yes
IdentityFile ~/.ssh/id_ed25519_exe
Step 2 — Connect (on your laptop, in a second terminal):
remote-viewer spice://localhost:5900
You should see the XFCE desktop, auto-logged in as the vagrant user.
What You Get
| Feature | Details |
|---|---|
| Clipboard sharing | Copy/paste between host and guest via spice-vdagent |
| Dynamic resolution | Drag-resize the remote-viewer window and the guest follows |
| Guest OS | Debian 12 (generic/debian12 Vagrant box) |
| Desktop | XFCE 4.18 with LightDM |
| Auto-login | vagrant user, no password prompt |
| Video | QXL with 64 MB VRAM |
| Resources | 2 vCPUs, 2 GB RAM |
VM Management
All commands should be run from the repo directory. Prefix with sg libvirt -c if your shell doesn't have the libvirt group active.
vagrant status # check VM state
vagrant ssh # SSH into the guest
vagrant halt # graceful shutdown
vagrant up # start the VM
vagrant reload # restart the VM
vagrant destroy # delete the VM entirely
vagrant provision # re-run the provisioning script
Files
| File | Purpose |
|---|---|
Vagrantfile |
VM definition — Debian 12, XFCE, SPICE graphics |
playbook.yml |
Ansible playbook — installs KVM, libvirt, Vagrant |
inventory.ini |
Ansible inventory (defaults to localhost) |
run.sh |
Launcher script for the Ansible playbook |
Installed by the Ansible Playbook
| Component | Details |
|---|---|
| KVM / QEMU | qemu-kvm, hardware acceleration via /dev/kvm |
| libvirt | libvirt-daemon-system, libvirt-clients, virtinst, bridge-utils |
| Vagrant | From the official HashiCorp APT repository |
| vagrant-libvirt | Plugin built from source (libvirt-dev, ruby-dev, etc.) |
Troubleshooting
"Permission denied" connecting to libvirt
Your shell doesn't have the libvirt group active. Either:
- Prefix commands with
sg libvirt -c "..." - Log out and back in
"incomplete link header" from remote-viewer
You're connecting through the exe.dev HTTPS proxy, which only handles HTTP. Use the SSH tunnel instead (see Connect to the Desktop).
Provisioning is slow
The first vagrant up downloads the Debian box (~800 MB) and installs XFCE packages. Subsequent starts are fast.
Guest resolution doesn't auto-resize
Make sure spice-vdagent is running inside the guest:
vagrant ssh -c "systemctl status spice-vdagentd"