In my previous post, I discussed what Vagrant is, some details about getting it installed, and then the structure of a basic Vagrant deployment.
In this post, we’ll cover in a bit more depth how to define virtual machines in Vagrant using a Vagrantfile.
If you recall from that previous post, we initialized a VM using Vagrant’s
init command. I mentioned that this creates a basic configuration. But what is that exactly? And where is it stored? The answer is: The
Vagrantfile. And by default it is stored in the current working directory — the directory from which you run
Everything Vagrant does centers around this file. It should be simply named “Vagrantfile” without any file extension.
So, when we do an
init, like with did with
vagrant init ubuntu/bionic64, Vagrant creates this file for us if it does not already exist. This default file Vagrant creates is very well-documented, and worth reading through to get familiar with the various configuration options.
For our purposes here, we are primarily concerned with the following three lines:
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" end
This is a distillation of the entire default file. Any of the other lines, starting with a hash (
#), are considered commments.
The first line of our distilled version tells Vagrant to use version 2 configuration (
Vagrant.configure("2")), and to proceed with iterating through the configuration parameters for the virtual machine (
do |config|) specified in the format
PARAMETER is just a generic placeholder for any of a number of documented machine settings. In our case, we have only one configuration parameter, called
box, for which we’ve specified
"ubuntu/bionic64". We then end the config block with the Ruby
So, as you may imagine, every Vagrant development environment requires you to specify a base image — what Vagrant calls a “box”, and what is specified as “ubuntu/bionic64” in the configuration we are using here. But how do you know what boxes are available and how to specify them?
HashiCorp has taken care of this with “Vagrant Cloud,” a publicly available registry of Vagrant boxes that you can search.
Each box has a version history as well as a snippet of configuration text that you can use to jumpstart your environment, like this:
Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" end
Looks familiar, doesn’t it?
You can specify any of a number of Providers to define the virtualization platform on which Vagrant deploys your virtual machines.
Vagrant “ships out of the box with support for VirtualBox, Hyper-V, and Docker” and, by default, VirtualBox is the default provider it uses, which we can see in the output of our first
Bringing machine 'default' up with 'virtualbox' provider... ==> default: Importing base box 'ubuntu/bionic64'...
While VirtualBox is the default and we could simply add configuration without specifying it, we want to add the provider explicity, so that it is clear what we intend and what Vagrant is actually going to modify. Thus, we add this block:
config.vm.provider "virtualbox" do |v| end
Vagrantfile so far, look like this:
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" # Generic VM configuration goes here config.vm.provider "virtualbox" do |v| # VirtualBox-specific configuration goes here end end
Note: If we want to allow for multiple providers, but prefer one over another we can specify the preference order like this:
# Preference order runs top-down, first match preferred # Prefer Hyper-V over VMware Fusion # and prefer VMware Fusion over VirtualBox config.vm.provider "hyperv" config.vm.provider "vmware_fusion" config.vm.provider "virtualbox"
Now that we have VirtualBox specified, let’s configure a few options we would typically set whenever we create a VM manually: processor, memory, and networking.
While some settings can be added irrespective of the specific provider, most are provider-specific and need to be added in the provider block. In other cases, they can be either.
For example, we could add this line to specify a static IP address on the private network:
config.vm.network "private_network", ip: "192.168.56.100"
In context, here’s how it would look:
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" # Generic VM configuration goes here config.vm.network "private_network", ip: "192.168.56.100" config.vm.provider "virtualbox" do |v| # VirtualBox-specific configuration goes here end end
Notice that we have it outside the
"virtualbox" provider block, with generic VM configuration.
This can alternatively (and somewhat confusingly, perhaps) be configured specifically at the provider level, like this:
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" # Generic VM configuration goes here config.vm.provider "virtualbox" do |v| # VirtualBox-specific configuration goes here v.network :private_network, ip: "192.168.56.100" end end
Note how the
v.network parameter is instead nested in the
"virtualbox" provider block now.
You will see this specified both ways in other people’s Vagrantfiles, so it’s important to note that anything not specified in the provider-specific block (here
"virtualbox") can apply to any provider used, often with fewer customization options.
CPU and Memory
To specify the amount of memory and virtual CPUs we want to have assigned to our VM, we can simply add a couple of like-named parameters to the VirtualBox configuration section, as follows:
v.cpus = 2 v.memory = 1024 # Megabytes
Note that memory is specified in megabytes.
Vagrantfile so far:
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" config.vm.provider "virtualbox" do |v| v.cpus = 2 v.memory = 1024 v.network :private_network, ip: "192.168.56.100" end end
This takes care of basic configuration in our Vagrantfile. Next time, we’ll discuss how to specify any post-deployment configuration we want to perform on our virtual machines with Provisioners….
- Vagrantfile Machine Settings
- Vagrant Boxes
- Vagrant Cloud
- Vagrant Providers
- Vagrant Provisioners