Michael Trojanek (relativkreativ) — Bootstrapper and creator of things

This article was published on December 15th 2014 and takes about 7 minutes to read.

Use it with caution — it is probably still valid, but it has not been updated for over a year.

How to build a Vagrant base box from a VirtualBox VM

Sometimes the publicly available Vagrant boxes are just not what you need. Building a base box yourself takes some time, but it is well worth the effort if you need virtual machines often.

Vagrant is a tremendously useful tool for developers and operators. It allows you to spin up a virtual machine (or even a whole network of VMs) with basically one command.

While working on Efficient Rails DevOps, I needed to quickly spin up a virtual machine with a minimal CentOS 7 installation (and more importantly: enable readers to do so) but I could not find a suitable machine on atlas.hashicorp.com (formerly vagrantcloud.com) to build upon.

That's why I decided to build my own base box. This task is not trivial, so I want to share my knowledge with this step-by-step tutorial in case you want to build your own Vagrant base box.

Prepare a virtual machine

If you haven't installed VirtualBox yet, download it from here and install it.

When you are done, create a new virtual machine by clicking the New button in VirtualBox' GUI.

Per convention, Vagrant boxes should adhere to some guidelines — you do not have to follow these rules, but other users will thank you when you release your box for public use.

First of all, Vagrant boxes should be named vagrant-{distribution}-{version}. As I will build a minimal CentOS 7 box in this article, so I name it vagrant-centos-7-minimal.

In terms of system resources, keep the machine minimal — number of CPUs and RAM size can later easily be changed in each user's Vagrantfile. I will use one CPU and 1024MB of RAM (less RAM will not allow you to use the graphical installer).

Choose VirtualBox' own format for dynamically allocating a new virtual harddisk (VDI (VirtualBox Disk Image)) and give it a reasonable size to enable users the installation of everything they need. As the virtual harddisk will grow with it's contents, its size does not matter (in terms of the virtual disk's file size). I usually go with 40GB.

We are now almost done. With the new machine selected, click Settings in the toolbar and make the following adjustments:

The virtual machine is now configured properly and we can setup the operating system.

Install the operating system

Now it's time to get hold of an ISO image of your distributions installation media. I downloaded the CentOS-7-x86_64-Minimal-1503-01.iso image from one of my local mirrors listed at CentOS' download page.

Now start the VM and when VirtualBox prompts you for a optical disk file or a physical optical drive containing installation media, point it to the ISO you just downloaded.

CentOS now starts the graphical installer, where we can go with the default values (however, feel free to change the server's time zone and keyboard layout). Do not forget to enable the network interface and configure it to use DHCP to obtain an IP address.

The root password on Vagrant boxes should be vagrant, so set it accordingly. Additionally, create a vagrant user with the same password, which will later be used to connect to the machine.

Finishing touches

When the installation has finished, there are some small tasks left to do before we can package the machine:

Vagrant's documentation suggests that you alter the SSH daemon's configuration to avoid reverse DNS lookups. To accomplish this, login as root, open /etc/ssh/sshd_config, find the line which reads #UseDNS yes and change it to UseDNS no.

Restart the SSH daemon with systemctl restart sshd.service after that. And while we're at it, make sure that the SSH daemon is started when the VM boots by issuing the command systemctl enable sshd.service.

Using the visudo-command, add the line following line at the and of the sudoers file:

vagrant ALL=(ALL) NOPASSWD:ALL

Additionally, remove the line Defaults requiretty which will enable Vagrant to use sudo without a TTY (you guessed it).

Next up is configuring the vagrant user's password less authentication. Our minimal machine has no way of downloading files from the web at the moment, so you need to install wget with yum install wget.

Then open a login shell for the vagrant user (su - vagrant), download its public key and set the right permissions:

mkdir -m 0700 /home/vagrant/.ssh
wget --no-check-certificate https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub -O /home/vagrant/.ssh/authorized_keys
chmod 0600 /home/vagrant/.ssh/authorized_keys

Leave the vagrant user's shell and update all packages with yum update. You'll want to reboot the machine now as yum probably installs a new kernel and we want to compile VirtualBox' guest additions with the newest kernel available.

When your virtual server has rebooted, install the packages needed for compiling the VirtualBox guest additions which Vagrant needs to configure shared folders:

yum install bzip2 gcc kernel-devel perl

Then select Devices ? Insert Guest Additions CD image… from VirtualBox' menu which enables you to mount the filesystem and install the guest additions:

mount /dev/cdrom /media
/media/VBoxLinuxAdditions.run
umount /media

I found out that when using Vagrant 1.7.4 with VirtualBox 5.0.10 r10406, the VM fails to load the vboxsf kernel module when booting the server. To find out if you suffer from this problem, reboot the server and then run lsmod | grep vboxsf. If you do not see the vboxsf module loaded, use the command echo vboxsf > /etc/modules-load.d/vboxsf.conf to force loading at boot time.

You can now reduce the VMs file size by running the following commands. They fix fragmentation issues and allow the machine to be compressed more efficiently (I managed to reduce the packaged box' size from 585MB to 492MB):

dd if=/dev/zero of=/EMPTY bs=1M
rm -f /EMPTY

Congratulations — your virtual machine is now ready to be packaged by Vagrant after powering it off with the poweroff-command.

Package the Vagrant box

As I write this article, Vagrant's current version is 1.7.2 and unfortunately, its package command is broken (see this ticket). If you do not want to wait for the release of a fix, you can monkeypatch Vagrant yourself:

Open the file machine.rb in Vagrant's lib subdirectory (on Mac OS X that's /opt/vagrant/embedded/gems/gems/vagrant-1.7.1/lib/vagrant/machine.rb) and change line 153 from def action(name, **opts) to def action(name, opts).

Now you are able to package the machine. Switch to your VMs subdirectory in VirtualBox' default folder (~/VirtualBox VMs/vagrant-centos-7-minimal) and run the following command:

vagrant package --base vagrant-centos-7-minimal

vagrant-centos-7-minimal is the machine's name as you have set it in VirtualBox' GUI.

Packaging needs some time, but soon you will see that a file called package.box has been created in this directory.

Use this box

You can now import this base box into Vagrant with the command vagrant box add vagrant-centos-7-minimal package.box.

After that, you can init a Vagrant box with vagrant init vagrant-centos-7-minimal in any directory and use it like any other Vagrant box. This means that from now on, creating a Vagrant base box with a minimal CentOS 7 installation will take less than a minute.

If a minimal CentOS 7 box is all you need, I published the box I built for this article on HashiCorp's Atlas, which means that you can use it straight away by initializing Vagrant with vagrant init relativkreativ/centos-7-minimal. If you are in search for a CentOS 6 box, use vagrant init relativkreativ/centos-6-minimal (I configured this one in the exact same way, but using a CentOS 6.6 minimal image).

Level up!

There is a whole lot more you can do with Vagrant — especially when developing Rails applications.

Join my email list and I will send you the download link for a free sample chapter of Developing With Vagrant, a book which will teach you some nifty techniques.

No spam — guaranteed. You can leave at any time.