The End of a Long Relationship: What Prompted the Split
Benjamin Toll used Vagrant for over a decade. Starting in 2010, he built his entire local virtual machine workflow around it — first through VirtualBox, then migrating to libvirt and KVM as his technical needs matured. That progression alone signals something important: this is not a developer who bounced off Vagrant after a rough afternoon. This is someone who grew with the tool, adapted alongside it, and invested real time mastering its abstractions.
The decision to walk away didn’t arrive as a single breaking point. It accumulated. Friction built across multiple layers of the development environment stack — provisioning behavior, network connectivity, filesystem mounting, SSH agent forwarding. Each issue, taken alone, was manageable. Taken together, they formed a pattern that Vagrant’s convenience layer couldn’t paper over anymore.
Toll’s own diagnosis cuts to the core of it: Vagrant had become “too much software.” That phrase carries weight coming from someone operating at his level. It isn’t a complaint about complexity in general — it’s a specific recognition that the abstraction Vagrant provides costs more than it returns once you understand the underlying systems well enough to work with them directly. Tools like KVM, libvirt, and virsh stopped being intimidating black boxes and started looking like cleaner, more direct paths to the same destination.
That shift in perspective is happening across the power-user community. Vagrant built its reputation on making virtual machine management accessible to developers who didn’t want to think about hypervisors. That value proposition holds for newcomers. But developers who have since learned how Linux virtualization actually works — who understand what a Vagrantfile is hiding from them — increasingly find the hiding annoying rather than helpful.
The quiet exodus from Vagrant isn’t driven by any single competitor winning the local dev environment war. It reflects a maturation curve. Developers outgrow the tool’s assumptions, hit the ceiling of its provider model, and start questioning why they’re routing commands through an intermediary when the destination is right there. Vagrant’s long relationship with the developer community is ending not with a scandal, but with a shrug — and that might be the more damning verdict.
Going Closer to the Metal: KVM, libvirt, and virsh
For some developers, the answer to Vagrant’s bloat isn’t another abstraction layer — it’s the removal of abstraction entirely. KVM (Kernel-based Virtual Machine) is baked directly into the Linux kernel, and libvirt provides a clean API to manage it without any Ruby DSL standing in the middle. virsh, the command-line interface for libvirt, hands you direct control over virtual machine lifecycle: defining domains, starting and stopping instances, managing virtual networks, and inspecting guest state — everything Vagrant was wrapping behind a layer of vagrant up convenience.
Benjamin Toll, a developer who used Vagrant from 2010 through both VirtualBox and libvirt backends, eventually concluded that Vagrant had become “too much software.” That’s a precise diagnosis. When you’re already running KVM and libvirt on Linux, Vagrant isn’t giving you virtualisation — it’s giving you a vocabulary on top of virtualisation you already control. Once that vocabulary stops being an asset and starts being a source of failures, the engineering response is to cut it.
The shift to managing VMs directly through virsh forces you to understand what Vagrant was doing on your behalf: preseeding OS installations, configuring virtual network bridges, handling filesystem mounts, and forwarding SSH agents. These aren’t trivial operations. But when Vagrant’s handling of them breaks — and it does break, particularly around network connectivity, mounting, and SSH agent forwarding — debugging through the abstraction is slower than debugging without it.
This is where local VM management with raw KVM tooling reveals a different kind of developer: one who prefers understanding the Linux virtualisation stack over relying on a tool that hides it. The libvirt daemon, QEMU as the hardware emulator, and virsh as the operator’s interface form a stack that doesn’t change behavior unexpectedly between Vagrant plugin versions.
The broader lesson applies beyond any single tool. Abstractions exist to save time. When they stop doing that — when the wrapper becomes the bottleneck — peeling it back isn’t regression. It’s calibration.
The Pain Points Vagrant Couldn’t Fix: Preseeding, Networking, and Mounting
Vagrant’s abstraction layer was supposed to make life easier. For a certain class of developer working with straightforward VirtualBox setups, it did. But power users who pushed into more complex workflows discovered that the same abstraction that simplified onboarding actively blocked serious configuration work.
Preseeding — the mechanism Debian and Ubuntu use to automate installer responses — is a clear example. Vagrant’s opinionated provisioning layer sits between the developer and the raw VM boot process, making it awkward to inject preseed files cleanly. Developers who wanted fully automated, unattended Debian installations found themselves writing workarounds rather than writing configuration. Working directly with KVM, libvirt, and virsh eliminates that friction entirely. The preseed file reaches the installer without Vagrant mediating the process.
Network configuration exposed a similar structural problem. Vagrant abstracts away the underlying network setup, which works fine until it doesn’t — and when it breaks, debugging becomes a hunt through multiple layers of indirection. Developers running libvirt-backed VMs directly through virsh can define network interfaces, bridges, and NAT rules with explicit control. Vagrant’s networking model turned routine diagnostics into archaeology.
Shared folder mounting is where Vagrant’s reputation took the most sustained damage. VirtualBox Guest Additions created version mismatch headaches. The vagrant-vbguest plugin existed specifically to paper over this problem. Switching to the libvirt provider brought a different set of mounting complications, often requiring the vagrant-sshfs plugin or NFS configuration that varied inconsistently across host operating systems. File sync performance on large codebases was poor, and the failure modes were opaque. Developers running local VMs through native virtualization tools bypass this entirely — mounting is handled through standard mechanisms with predictable, documented behavior.
Each of these friction points shares a common structure: Vagrant added a layer of automation that served beginners but created a ceiling for experienced users. The developers abandoning local VM management tools like Vagrant aren’t doing so because virtualization is dead — they’re doing so because they’ve learned enough to find the abstraction costly rather than helpful.
The Missing Story: SSH Agent Forwarding and Security Workflows
SSH agent forwarding is one of those features that separates developers who understand their toolchain from developers who just use it. The mechanism lets you authenticate to remote servers using your local SSH keys without ever copying private key files into a virtual machine — a critical distinction when those keys have access to production infrastructure.
Vagrant handles this automatically when you set config.ssh.forward_agent = true in your Vagrantfile. One line, done. That convenience is exactly where the hidden risk lives. Most tutorials covering Vagrant local development environments mention this setting without explaining what it actually does: it exposes your local SSH agent socket inside the VM, meaning any process running in that VM can potentially use your keys to authenticate elsewhere. If the VM is compromised, or if a provisioning script does something unexpected, your keys are reachable.
Developers ditching Vagrant for direct KVM and libvirt setups — or for containerized workflows — have to implement agent forwarding deliberately. In practice, that means configuring ForwardAgent yes for specific hosts in ~/.ssh/config, or passing -A explicitly when connecting via ssh. Neither approach is difficult, but both require the developer to make a conscious decision about when and where forwarding is active.
That conscious decision is the point. When you configure SSH agent forwarding manually, you know which hosts receive forwarded credentials, you can scope it narrowly, and you have a clear mental model of the trust boundary. Vagrant’s abstraction collapses that visibility. The setting works, but the developer loses the habit of thinking about it.
For local development environments handling real deployment workflows — pushing to staging, cloning from private repositories, running Ansible against live hosts — this detail is not minor. Developers building infrastructure tooling on top of their dev environments need the same security hygiene they would apply in CI/CD pipelines. Vagrant’s convenience abstractions optimized for onboarding speed work against that discipline. Replacing Vagrant with explicit tooling forces the uncomfortable but necessary exercise of understanding what the environment actually does with your credentials.
What This Really Means for the Dev Tooling Landscape
Vagrant’s quiet exit from expert workflows exposes something bigger than one tool’s obsolescence. The abstraction layer it represented — a Ruby DSL sitting on top of VirtualBox, then libvirt and KVM, smoothing over the rough edges of VM provisioning — made sense in 2010, when most developers had limited exposure to virtualization primitives. That era is over.
The instinct driving developers toward raw libvirt, virsh, and direct KVM management isn’t nostalgia for complexity. It’s a recognition that Vagrant’s abstraction now costs more than it saves. When a developer hits a wall with network bridge configuration, preseed automation, or SSH agent forwarding behavior, Vagrant adds a debugging layer that obscures the actual system behavior underneath. Stripping that layer out means working directly with tools that have complete, stable interfaces and predictable behavior.
Docker and Kubernetes don’t fill this gap. Container runtimes solve the application packaging and orchestration problem — they don’t replace the need for isolated, full-system virtual machines running their own kernels for OS-level testing, network simulation, or kernel development workflows. The local VM use case remains real and alive. What’s missing is a modern, lightweight workflow for it.
The developers moving away from Vagrant and toward direct hypervisor tooling gain genuine reproducibility through shell scripts, cloud-init configurations, and preseeding — all portable, auditable, and version-controllable without a proprietary abstraction in the middle. That control comes at a cost: the documentation for libvirt, virsh, and QEMU is scattered, the learning curve is steep, and there’s no polished onboarding experience equivalent to a Vagrantfile.
This creates a real opening in the local development environment space. The tooling landscape around virtual machine management for developers remains fragmented in a way that container tooling no longer is. Whoever builds a developer-focused VM workflow layer that sits closer to the metal than Vagrant — without inheriting its legacy weight — addresses a problem that hasn’t been cleanly solved. Until then, experienced developers are writing their own scripts and accepting the tradeoff.