logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·02.11.26
← Back to news

Ditching Vagrant: VMs with KVM and Virsh on Debian

Original article
benjamintoll.com|72 points|30 comments|by fanf2|Jul 6, 2026

Moving Beyond Vagrant: Embracing KVM and Virsh on Debian

"Goodbye, old friend."

Since 2010, Vagrant has been my steadfast companion for virtual machine (VM) management. It guided me through my early days with VirtualBox and eventually transitioned with me toward libvirt and KVM. However, the time has come to part ways.

Introduction

To be perfectly honest, I've come to realize that Vagrant is simply too much software for my specific needs. As I evolve in my technical journey, I find myself questioning the architectural choices I made when I was less experienced. I have a habit of revisiting old projects to see where I can optimize, and this workflow was no exception.

The Evolution of my Workflow

  • 2010: Started with Vagrant for basic VM lifecycle management.
  • Mid-journey: Integrated Ansible via Vagrant's built-in support for provisioning.
  • Recent: Questioning the need for an abstraction layer.

Why rely on a third-party tool when the Linux kernel has included KVM since version ≈2.6.20\approx 2.6.20≈2.6.20? The tools for creating and managing VMs are already native to the OS. Adding another layer of abstraction felt redundant.

I initially tried a "half-measure" by switching the Vagrant provider from VirtualBox to libvirt, but I was distracted by other "priorities." Specifically, I was doing the most important work of my life grinding relentlessly for a previous employer. I spent my nights waking up at 3:00 AM to patch fragile infrastructure that we were never given the time to properly build—all in the name of Agile and the Scrum Master, because apparently, stability doesn't "provide value to the customer."

Eventually, I broke that cycle, reclaimed my time, and decided to do things the right way. I uninstalled Vagrant 2, and miraculously, my domestic bliss returned.


Understanding the Stack: KVM, libvirt, and virsh

To understand the replacement, we need to look at the hierarchy of the virtualization stack:

Component Breakdown

ToolRoleDescription
KVMHypervisorA kernel module that turns Linux into a hypervisor, emulating hardware in software.
libvirtManagementA library and daemon (pronounced demon) that provides a unified API for KVM, Xen, LXC, and QEMU.
virshInterfaceThe command-line frontend for libvirt.

Verifying KVM

You can check if KVM is active on your system using lsmod:

$ lsmod | ag kvm
kvm_intel 380928 0 kvm
kvm 1146880 1 kvm_intel
kvm_intel irqbypass 16384 1 kvm

Alternatively, just check for the device file: ls /dev/kvm.

Installation

To get the management daemon running: sudo apt-get install libvirt-daemon-system

To install the CLI tools (because real engineers avoid GUIs like virt-manager whenever a CLI exists): sudo apt-get install libvirt-clients virtinst (Note: This also pulls in virt-install).

Essential virsh Cheat Sheet

VM (Domain) Information:

  • dominfo, domid, domname, domstate, domuuid
  • domblkinfo, domblkstat (Storage)
  • domiflist, domifstat (Network)
  • dommemstat (Memory)

Host/Node Information:

  • capabilities, hostname, nodeinfo

Management Actions:

  • connect, list, start, shutdown, reboot, destroy, undefine, edit, dumpxml

For those wanting to dive deeper, I recommend exploring topics like Unsharing Namespaces and Linux Container Networking.


The "Ruh-Roh" Moment

Everything was sailing smoothly until I upgraded my system from Debian Bookworm to Trixie. Suddenly, my serial output vanished when booting VMs.

I was using virt-install to pass kernel boot parameters to enable host-to-VM communication via the serial port, but the parameters weren't sticking in the bootloader.

Troubleshooting the Bootloader

To verify what is actually being passed to the kernel, you must check the GRUB configuration inside the VM: cat /etc/default/grub

The critical lines should look like this:

GRUB_CMDLINE_LINUX_DEFAULT="quiet"
GRUB_CMDLINE_LINUX="console=ttyS0,115200"

If you need to manually correct these, apply the changes and update the config:

$ sudo grub-mkconfig -o /boot/grub/grub.cfg
$ sudo reboot

A Better Path: Preseeding

Rather than spending hours debugging why a specific version of virt-install stopped behaving, I decided to pivot. I am now moving toward using preseeded virtual machines.

By passing a configuration file during the installation process, the build becomes:

  1. Deterministic: The same input always produces the same VM.
  2. Versionable: Configs can be stored in Git.
  3. Reliable: You have a guarantee that specified values are actually written to the machine.

Virtualization Concept


References:

  • On Virtual Machines
  • On Unsharing Namespaces (Parts 1 & 2)
  • On Linux Container Networking