Use Packer to build a custom Vagrant Box, from another Vagrant Box

Posted on June 1, 2017

This post will demonstrate:

Context

Packer is really great at building images, and there are “builders” for all the various platforms (vmware, virtualbox, aws, docker, etc). Each of these builders start with an “input”, either an ISO to boot and install with, or a source image to use as a foundation. If you’ve used packer, you’ve probably automated the OS install for some Linux distro or BSD out there. It’s great that it can be done, but for some tasks, I’d like to start with an existing foundation.

Vagrant has a concept of “boxes”, which is similar to an “image” in the container ecosystem (with each VM created from that template box as the “container” in the analogy). There are many existing boxes, so if you want to build a local dev environment for your peers, or want a place to test your configuration management formula, wouldn’t it be nice to start the packer build with an existing box?

This is all great, however, there isn’t a clear and documented route to using an existing vagrant box as the source for a packer build. It’s still possible, here is how I did it.

Initial Setup

download & install:

For me, that looked like:

ᐅ wget https://releases.hashicorp.com/vagrant/1.9.5/vagrant_1.9.5_x86_64.deb
ᐅ sudo dpkg --install vagrant_1.9.5_x86_64.deb
ᐅ vagrant version
Installed Version: 1.9.5
Latest Version: 1.9.5

 You're running an up-to-date version of Vagrant!
ᐅ wget https://releases.hashicorp.com/packer/1.0.0/packer_1.0.0_linux_amd64.zip
ᐅ unzip packer_1.0.0_linux_amd64.zip -d ~/bin/
ᐅ packer version
Packer v1.0.0
ᐅ wget http://download.virtualbox.org/virtualbox/5.1.22/virtualbox-5.1_5.1.22-115126\~Ubuntu\~trusty_amd64.deb
ᐅ sudo dpkg --install virtualbox-5.1_5.1.22-115126\~Ubuntu\~trusty_amd64.deb
ᐅ virtualbox --help | head -n 2
Oracle VM VirtualBox Manager 5.1.22
(C) 2005-2017 Oracle Corporation

Got everything? Great!

Grab the box

Let’s say you have chosen the ubuntu/trusty64 box as the foundation for the packer build.

Grab it!

ᐅ vagrant box add ubuntu/trusty64 --provider virtualbox
==> box: Loading metadata for box 'ubuntu/trusty64'
    box: URL: https://atlas.hashicorp.com/ubuntu/trusty64
==> box: Adding box 'ubuntu/trusty64' (v20170530.0.1) for provider: virtualbox
    box: Downloading: https://atlas.hashicorp.com/ubuntu/boxes/trusty64/versions/20170530.0.1/providers/virtualbox.box
==> box: Successfully added box 'ubuntu/trusty64' (v20170530.0.1) for 'virtualbox'!

The OVF

Now, we’ve “imported” this box into vagrant, but we need to tell Packer about a disk image it can use for the build (like an OVF file).

If we poke around Vagrant’s .path, we’ll find that OVF:

ᐅ ls -Alh ~/.vagrant.d/boxes/ubuntu-VAGRANTSLASH-trusty64/20170530.0.1/virtualbox
total 426M
426M Jun  1 07:41 box-disk1.vmdk
 11K Jun  1 07:40 box.ovf
  25 Jun  1 07:41 metadata.json
 505 Jun  1 07:40 Vagrantfile

If you remember, we imported the 20170530.0.1 version of the ubuntu/trusty64 box. The pattern here is simple to see: ~/.vagrant.d/boxes/$REPO-VAGRANTSLASH-$BOX/$VERSION/$VM_PROVIDER/box.ovf

Packer build template

Let’s plug this into an example build template. We will use the virtualbox-ovf builder to import the existing Vagrant box, some provisioners to customize that box, and finally the vagrant post-processor to export our customizations as a new box that we can import into Vagrant:

{
  "variables": {
    "home":    "{{env `HOME`}}",
    "out_dir": "output-box",
    "name":    "base-host",
    "box":     "trusty64",
    "repo":    "ubuntu",
    "version": "20170526.4.0"
  },
  "builders": [{
    "type": "virtualbox-ovf",
    "source_path": "{{user `home`}}/.vagrant.d/boxes/{{user `repo`}}-VAGRANTSLASH-{{user `box`}}/{{user `version`}}/virtualbox/box.ovf",
    "ssh_username": "vagrant",
    "ssh_password": "vagrant",
    "ssh_wait_timeout": "90s",
    "shutdown_command": "echo 'packer' | sudo -S shutdown -P now"
  }],
  "provisioners": [ ... ],
  "post-processors": [{
    "type": "vagrant",
    "keep_input_artifact": true,
    "output": "{{user `out_dir`}}/{{user `name`}}-{{user `repo`}}-{{user `box`}}-{{user `version`}}.box"
  }]
}

Import into Vagrant!

ᐅ vagrant box add base-host output-box/base-host-ubuntu-trusty64-20170526.4.0.box

Helpful Resources