Craig Forrester

The Pleasure of Finding Things Out

github linkedin email rss
Getting Started with Vagrant: Vagrantfile
January 8, 2020
5 minutes read

Recap

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 vagrant init.

Vagrantfile

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 config.vm.PARAMETER, where 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 end statement.

Boxes

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?

Providers

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 vagrant up:

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

The complete 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.

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.

Our 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….

Additional Reading


Back to posts