No description
Find a file
exe.dev user 0a11829114 Install provisioned configs with shell provisioner
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.
2026-06-08 20:54:20 +00:00
provision Install provisioned configs with shell provisioner 2026-06-08 20:54:20 +00:00
.gitignore Ignore .vagrant via .gitignore 2026-06-08 19:18:04 +00:00
inventory.ini Ansible playbook: Vagrant + libvirt/KVM setup 2026-06-08 12:48:49 +00:00
playbook.yml Ansible playbook: Vagrant + libvirt/KVM setup 2026-06-08 12:48:49 +00:00
README.md Format README tables and diagram 2026-06-08 19:16:47 +00:00
run.sh Ansible playbook: Vagrant + libvirt/KVM setup 2026-06-08 12:48:49 +00:00
Vagrantfile Install provisioned configs with shell provisioner 2026-06-08 20:54:20 +00:00

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:

  1. Ansible playbook — provisions a bare Ubuntu host with KVM, libvirt, and Vagrant
  2. 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 of virt-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

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 the libvirt group 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

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"