Install Vagrant and VirtualBox: Create Local Virtual Machines as Code
Why Vagrant and VirtualBox #
VirtualBox runs virtual machines on your computer. Vagrant describes those machines in a Vagrantfile, so a whole lab (three servers, their IPs and their software) starts with vagrant up and disappears with vagrant destroy. It is the base of the Kubernetes cluster built with Ansible and Vagrant.
Install on Ubuntu #
sudo apt update
sudo apt install -y virtualbox
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install -y vagrant
vagrant --version
The repository is signed with GnuPG, which is why the key is imported first. On macOS use brew install --cask virtualbox vagrant; on Windows use the installers. Hardware virtualization (VT-x or AMD-V) must be enabled in the BIOS, and other hypervisors that use it (KVM, Hyper-V) can conflict with VirtualBox.
First machine #
mkdir lab && cd lab
vagrant init ubuntu/jammy64
vagrant up
vagrant ssh
vagrant init writes a Vagrantfile. The first vagrant up downloads the box (the base image of the VM) and boots it. vagrant ssh opens a shell inside.
A lab with three servers #
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/jammy64"
config.vm.provider "virtualbox" do |vb|
vb.memory = 1024
vb.cpus = 1
end
(1..3).each do |i|
config.vm.define "node#{i}" do |node|
node.vm.hostname = "node#{i}"
node.vm.network "private_network", ip: "192.168.56.1#{i}"
end
end
config.vm.provision "shell", inline: <<-SHELL
apt-get update -y
apt-get install -y curl
SHELL
endprivate_networkcreates an interface on a host-only network, so the VMs reach each other and your computer reaches them, with fixed IPs. VirtualBox only allows ranges inside192.168.56.0/21unless you edit/etc/vbox/networks.conf.- The
shellprovisioner runs when the machine is created.
Provision with Ansible #
Replace the shell provisioner with a playbook (see Install Ansible):
config.vm.provision "ansible" do |ansible|
ansible.playbook = "site.yml"
end
Vagrant generates the inventory for you, with the three nodes.
Everyday commands #
vagrant status # state of every machine
vagrant up node1 # start only one
vagrant ssh node2
vagrant halt # shut down, keep the disks
vagrant provision # run the provisioners again
vagrant reload --provision # restart after changing the Vagrantfile
vagrant snapshot save clean # save a restore point
vagrant snapshot restore clean
vagrant destroy -f # delete everything
Tips #
- Commit the
Vagrantfileto Git: it is the documentation of your lab. /vagrantinside each VM is a shared folder with the project directory.- If
vagrant upfails with a network error, runVBoxManage list hostonlyifsand check the host-only interface exists. - For containers instead of VMs use Docker; for cloud servers use Terraform.