[Guide] Create VMS on Chromebook with Hashicorp Vagrant - Works on x86

This guide credits goes to jbowdre guide in: https://runtimeterror.dev/create-vms-chromebook-hashicorp-vagrant/

Create Virtual Machines on a Chromebook with HashiCorp Vagrant

Install the prerequisites

One Click for First install (not fixed yet skip this for now)

sudo apt update ; sudo apt install build-essential nano gpg lsb-release aptitude wget virt-manager libvirt-dev rsync -y ; sudo aptitude upgrade -y ; sudo gpasswd -a $USER libvirt ; newgrp libvirt && echo “remember_owner = 0” | sudo tee -a /etc/libvirt/qemu.conf && sudo systemctl restart libvirtd && wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg && echo “deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main” | sudo tee /etc/apt/sources.list.d/hashicorp.list && sudo apt update ; sudo aptitude install vagrant -y ; sudo aptitude upgrade -y ; vagrant plugin install vagrant-libvirt

There are are a few packages which need to be installed before we can move on to the Vagrant-specific stuff. It’s quite possible that these are already on your system… but if they aren’t already present you’ll have a bad problem1.

sudo apt update && sudo apt install build-essential nano gpg lsb-release aptitude wget -y

I’ll be configuring Vagrant to use libvirt to interface with the Kernel Virtual Machine (KVM) virtualization solution (rather than something like VirtualBox that would bring more overhead) so I’ll need to install some packages for that as well:

sudo apt install virt-manager libvirt-dev -y

And to avoid having to sudo each time I interact with libvirt I’ll add myself to that group:

sudo gpasswd -a $USER libvirt ; newgrp libvirt 

And to avoid this issue I’ll make a tweak to the qemu.conf file:

echo "remember_owner = 0" | sudo tee -a /etc/libvirt/qemu.conf 
sudo systemctl restart libvirtd

I’m also going to use rsync to share a synced folder between the host and the VM guest so I’ll need to make sure that’s installed too:

sudo apt install rsync -y

Install Vagrant

With that out of the way, I’m ready to move on to the business of installing Vagrant. I’ll start by adding the HashiCorp repository:

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update ; sudo aptitude install vagrant -y ; sudo aptitude upgrade -y

I also need to install the vagrant-libvirt plugin so that Vagrant will know how to interact with libvirt:

vagrant plugin install vagrant-libvirt 

Create a lightweight VM

Now I can get to the business of creating my first VM with Vagrant!

Vagrant VMs are distributed as Boxes, and I can browse some published Boxes at app.vagrantup.com/boxes/search?provider=libvirt (applying the provider=libvirt filter so that I only see Boxes which will run on my chosen virtualization provider). For my first VM, I’ll go with something light and simple: generic/alpine319.

So I’ll create a new folder to contain the Vagrant configuration:

mkdir vagrant-box; cd vagrant-box

And since I’m referencing a Vagrant Box which is published on Vagrant Cloud, downloading the config is as simple as:

vagrant init generic/alpine319

Before I vagrant up the joint, I do need to make a quick tweak to the default Vagrantfile, which is what tells Vagrant how to configure the VM. By default, Vagrant will try to create a synced folder using NFS and will throw a nasty error when that (inevitably2) fails. So I’ll open up the Vagrantfile to review and edit it:

nano Vagrantfile 

Most of the default Vagrantfile is commented out. Here’s the entirey of the configuration without the comments:

Vagrant.configure("2") do |config|
  config.vm.box = "generic/alpine319"
  config.nfs.verify_installed = false
  config.vm.synced_folder '.', '/vagrant', type: 'rsync'
end

With that, I’m ready to fire up this VM with vagrant up! Vagrant will look inside Vagrantfile to see the config, pull down the generic/alpine319 Box from Vagrant Cloud, boot the VM, configure it so I can SSH in to it, and mount the synced folder:

sudo vagrant up 

And then I can use vagrant ssh to log in to the new VM:

vagrant ssh 
cat /etc/os-release
ls -l
vagrant up 
vagrant halt 
vagrant destroy 

Create a heavy VM, as a treat

Having proven to myself that Vagrant does work on a Chromebook, let’s see how it does with a slightly-heavier VM… like Windows 11.

Space Requirement

Windows 11 makes for a pretty hefty VM which will require significant storage space. My Chromebook’s Linux environment ran out of storage space the first time I attempted to deploy this guy. Fortunately ChromeOS makes it easy to allocate more space to Linux (Settings > Advanced > Developers > Linux development environment > Disk size). You’ll probably need at least 30GB free to provision this VM.

Again, I’ll create a new folder to hold the Vagrant configuration and do a vagrant init:

mkdir vagrant-win11 ; cd vagrant-win11 ; vagrant init oopsme/windows11-22h2

And, again, I’ll edit the Vagrantfile before starting the VM. This time, though, I’m adding a few configuration options to tell libvirt that I’d like more compute resources than the default 1 CPU and 512MB RAM4:

nano Vagrantfile

Vagrant.configure("2") do |config|
  config.vm.box = "oopsme/windows11-22h2"
  config.vm.provider :libvirt do |libvirt|
    libvirt.cpus = 4 
    libvirt.memory = 4096
  end
end

Now it’s time to bring it up. This one’s going to take A While as it syncs the ~12GB Box first.

vagrant up 

Eventually it should spit out that lovely Machine booted and ready! message and I can log in! I can do a vagrant ssh again to gain a shell in the Windows environment, but I’ll probably want to interact with those sweet sweet graphics. That takes a little bit more effort.

First, I’ll use virsh -c qemu:///system list to see the running VM(s):

virsh -c qemu:///system list  
Id   Name                    State 
--------------------------------------- 
10   vagrant-win11_default   running

Then I can tell virt-viewer that I’d like to attach a session there:

virt-viewer -c qemu:///system -a vagrant-win11_default 

I log in with the default password vagrant, and I’m in Windows 11 land!

Next steps

Well that about does it for a proof-of-concept. My next steps will be exploring multi-machine Vagrant environments to create a portable lab environment including machines running several different operating systems so that I can learn how to manage them effectively with Salt. It should be fun!


  1. and will not go to space today. :leftwards_arrow_with_hook:
  2. NFS doesn’t work properly from within an LXD container, like the ChromeOS Linux development environment. :leftwards_arrow_with_hook:
  3. Through the magic of egrep -v "^\s*(#|$)" $file. :leftwards_arrow_with_hook:
  4. Note here that libvirt.memory is specified in MB. Windows 11 boots happily with 4096 MB of RAM… and somewhat less so with just 4 MB. Ask me how I know… :leftwards_arrow_with_hook:

Credits to jbowdre from this page for the guide:

Im trying to share it just so it doesnt die in the deep web.

Vagrant.configure("2") do |config|
  config.vm.provision "shell", before: :all, inline: "df -h /"

  config.vm.define "almalinux_9" do |config|
    config.vm.box = "almalinux/9"
    config.vm.disk :disk, primary: true, size: "50GB"
    config.vm.provision "shell", inline: "dnf install -y cloud-utils-growpart && growpart /dev/sda 4 && xfs_growfs /"
  end

  config.vm.define "debian_bookworm" do |config|
    config.vm.box = "debian/bookworm64"
    config.vm.disk :disk, primary: true, size: "50GB"
    config.vm.provision "shell", inline: "growpart /dev/sda 1 && resize2fs /dev/sda1"
  end

  config.vm.define "ubuntu_jammy" do |config|
    config.vm.box = "ubuntu/jammy64"
    config.vm.disk :disk, primary: true, size: "50GB"
  end

  config.vm.provision "shell", after: :all, inline: "df -h /"
end

vagrant up --provider=libvirt
vagrant status
vagrant destroy -f