Versioning for your local Vagrant boxes: adding a new box

I have been using Vagrant for quite some time now can’t tell you how much of a productivity boost it has been. All the VMs I have on my laptop are either powered by Vagrant, or feed into the Vagrant workflow.

One thing I haven’t worked out though is how to use versioning outside of Vagrant’s cloud. I don’t think I have what it takes to publish a good OS image publicly, and rather keep my boxes to myself to prevent others from injury.

My environment

While putting this post together I used the following software:

  • Ubuntu 20.04 LTS acts as my host operating system
  • Virtualbox 6.1.6
  • Vagrant 2.2.7

This is probably as current as it gets at the time of writing.

The need for box versioning

Vagrant saves you time by providing “gold images” you can spin up quickly. I prefer to always have the latest and greatest software available without having to spend ages on updating kernels and/or other components. As a result, I update my “gold image” VM from time to time, before packaging it up for Vagrant. Until quite recently I haven’t figured out how to update a VM other than delete/recreated it. This isn’t the best idea though, as indicated by this error message:

$ vagrant box remove debianbase-slim
Box 'debianbase-slim' (v0) with provider 'virtualbox' appears
to still be in use by at least one Vagrant environment. Removing
the box could corrupt the environment. We recommend destroying
these environments first:

default (ID: ....)

Are you sure you want to remove this box? [y/N] n 

This week I finally sat down trying to work out a better way of refreshing my Vagrant boxes.

As I understand it, box versioning allows me to update my base box without having to trash any environments. So instead of removing the box and replacing it with another, I can add a new version to the box. Environments using the old version can do so until they are torn down. New environments can use the new version. This works remarkably easy, once you know how to set it up! I found a few good sources on the Internet and combined them into this article.

Box versioning for Oracle Linux 7

As an Oracle person I obviously run Oracle Linux a lot. Earlier I came up with a procedure to create my own base boxes. This article features “oraclelinux7base” as the source for my Vagrant boxes. It adheres to all the requirements for Vagrant base boxes to be used with the Virtualbox provider.

Packaging the base box

Once you are happy to release your Virtualbox VM to your host, you have to package it for use with Vagrant. All my Vagrant boxes go to ~/vagrant/boxes, so this command creates the package:

$ vagrant package --base oraclelinux7base --output ~/vagrant/boxes/ol7_7.8.0.box
==> oraclelinux7base: Attempting graceful shutdown of VM...
==> oraclelinux7base: Clearing any previously set forwarded ports...
==> oraclelinux7base: Exporting VM...
==> oraclelinux7base: Compressing package to: /home/martin/vagrant/boxes/ol7_1.0.0.box 

In plain English this command instructs Vagrant to take Virtualbox’s oraclelinux7base VM and package it into ~/vagrant/boxes/ol7_7.8.0.box. I am creating this VM as the first OL 7.8 system, the naming convention seems optional yet I think it’s best to indicate the purpose and version in the package name.

At this stage, DO NOT “vagrant add” the box!

Creating box metadata

The next step is to create a little metadata describing the box. This time it’s not to be written in YAML, but JSON for a change. I found a few conflicting sources and I couldn’t get them to work until I had a look at how Oracle solved the problem. If you navigate to yum.oracle.com/boxes, you can find the links to their metadata files. I really appreciate Oracle changing to using versioning of their boxes, too!

After a little trial-and-error I came up with this file. It’s probably just the bare minimum, but it works for me in my lab so I’m happy to keep it the way it is. The file lives in ~/vagrant/boxes alongside the box file itself.

$ cat ol7.json
{
    "name": "ol7",
    "description": "Martins Oracle Linux 7",
    "versions": [
      {
        "version": "7.8.0",
        "providers": [
          {
            "name": "virtualbox",
            "url": "file:///home/martin/vagrant/boxes/ol7_7.8.0.box",
            "checksum": "db048c3d61c0b5a8ddf6b59ab189248a42bf9a5b51ded12b2153e0f9729dfaa4",
            "checksum_type": "sha256"
          }
        ]
      }
    ]
  } 

The file should be self-explanatory. The only noteworthy issue to run into is an insufficient number of forward slashes in the URL the URI is composed of “file://” followed by the fully qualified path to the box file, 3 forward slashes in total.

I used “sha256sum /home/martin/vagrant/boxes/ol7_7.8.0.box” to calculate the checksum.

Creating a VM

Finally it’s time to create the VM. I tend to create a directory per Vagrant environment, in this example I called it “versioning”. Within ~/vagrant/versioning I can create a Vagrantfile with the VM’s definition. At this stage, the base box is unknown to Vagrant.

$ nl Vagrantfile 
     1    # -*- mode: ruby -*-
     2    # vi: set ft=ruby :

     3    Vagrant.configure("2") do |config|
     4      config.vm.box = "ol7"
     5      config.vm.box_url = "file:///home/martin/vagrant/boxes/ol7.json"
     6      
     7      config.ssh.private_key_path = '/home/martin/.ssh/vagrantkey'

     8      config.vm.hostname = "server1"

     9      config.vm.provider "virtualbox" do |vb|
    10        vb.cpus = 2
    11        vb.memory = "4096"
    12      end

    13    end
 

The difference to my earlier post is the reference to the JSON file in line 5. The JSON file tells vagrant where to find the Vagrant box. The remaining configuration isn’t different from using non-versioned Vagrant boxes.

Based on this configuration file I can finally spin up my VM:

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'ol7' could not be found. Attempting to find and install...
    default: Box Provider: virtualbox
    default: Box Version: >= 0
==> default: Loading metadata for box 'file:///home/martin/vagrant/boxes/ol7.json'
    default: URL: file:///home/martin/vagrant/boxes/ol7.json
==> default: Adding box 'ol7' (v7.8.0) for provider: virtualbox
    default: Unpacking necessary files from: file:///home/martin/vagrant/boxes/ol7_7.8.0.box
    default: Calculating and comparing box checksum...
==> default: Successfully added box 'ol7' (v7.8.0) for 'virtualbox'!
==> default: Importing base box 'ol7'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'ol7' version '7.8.0' is up to date...
==> default: Setting the name of the VM: versioning_default_1588251635800_49095
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2200 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Setting hostname...
==> default: Mounting shared folders...
    default: /vagrant => /home/martin/vagrant/versioning 

Right at the beginning you can see that Vagrant loads “metadata for box ‘file:///home/martin/vagrant/boxes/ol7.json'” and then loads the box from the location specified in the JSON file.

Once the machine is started, I can also see it available for future use:

$ vagrant box list | grep ^ol7
ol7               (virtualbox, 7.8.0) 

The box is registered as ol7, using the Virtualbox provider in version 7.8.0.

Summary

In this post I summarised (mainly for my own later use ;) how to use box versioning on my development laptop. It really isn’t that much of a difference compared to the previous way I worked and the benefit will become apparent once you update the box. I’m going to cover upgrading my “ol7” box in another post.