Here I'm republishing an old blog post of mine originally from September 2016. The article has been slightly improved.

Vagrant: Creating a FreeBSD 11 base box (virtualbox) - pt. 2

The previous post discussed what Vagrant is, explained the installation (on FreeBSD), detailed the process of creating a new VM and briefly described how to install FreeBSD 11. The next step is to prepare the fresh installation for Vagrant and eventually create an actual base box from it. If you're new to VirtualBox or you don't know what a _provider_ is when it comes to Vagrant, you might want to read the first part. If you however already have a decent idea of virtualization and making sensible choices when installing FreeBSD, just do a fresh install in a VM and continue reading here.

Vagrant: Creating a FreeBSD 11 base box (virtualbox) - pt. 1

If you haven't read the first part: These two posts weren't written without a specific purpose. The idea was to dive into Bacula (a program for doing backups). For testing backups properly you need more than one machine obviously. I don't have enough spare PCs, so using Virtual Machines seemed like a good way of working around that limitation. Since Vagrant makes working with these VMs really easy, it totally makes sense to use it. I've used it quite a bit in the past and while I was at it again, I decided to blog about this great tool so that you maybe consider adding it to your tool set as well.

Preparing the VM for Vagrant: Settings

There are a few things on the side of the guest system that need to be in place so that Vagrant can work with it properly.

The first step is optional but considered good practice if you intend to create a public base box: Disable unneeded resources! To do so, go to the settings of your VM template in the VirtualBox GUI.

Selecting VM settings (PNG)

A good candidate to disable is audio support which is not generally needed in a Vagrant box.

Disabling audio for the VM (PNG)

Also deactivate USB support. It's enabled by default but is most likely unneeded in Vagrant boxes (there are other ways to share data between the VM and the host system).

Disabling USB for the VM (PNG)

Now fire up your fresh FreeBSD 11 system and log in as root. A little tweak that I suggest is to disable the auto delay of the boot loader. This will make the VM boot up 10 seconds faster because it won't wait for possible user interaction at the boot loader stage. To do so, you can use the following command:

# echo autoboot_delay="-1" >> /boot/loader.conf

Preparing the VM for Vagrant: Packages

If you're not using a new version that was just released a few days ago, it's always a good idea to update the base system first:

# freebsd-update fetch install

Reboot if the kernel was updated:

# shutdown -r now

The FreeBSD package manager (_pkg(8)_) ist not part of the base system (that way it can evolve faster) so let's bootstrap it first:

# env ASSUME_ALWAYS_YES=yes pkg install pkg

Vagrant needs _sudo_ to work. And not having _bash_ available works but doesn't make Vagrant happy. It's best to simply install both:

# pkg install -y sudo bash

Now configure sudo. The configuration file is _/usr/local/etc/sudoers_ on FreeBSD - but you may want to forget that again as you shouldn't touch that file by hand! Rather issue the command

# visudo

which will call a wrapper script that lets you edit the file and check it for validity before actually saving changes.

Why's that? Because on certain system configurations you can easily lock yourself out of the system with a defunct sudo (and you basically lose sudo when you ruin its configuration file)! Even though the script has vi in its name, that's just the default. You may want to set the EDITOR environment variable to another editor if you prefer emacs/nano/joe/whatever. Not liking vi is NOT an excuse for not using _visudo_!

When editing the sudoers file, look for the line "%wheel ALL=(ALL) NOPASSWD: ALL". It's commented out by default. Remove the comment sign and save. That's actually all that needs to be changed. What that change does: It grants all members of the _wheel_ group unrestricted sudo access without having to enter their password.

Depending on what kind of base box you want to create, you may want to install the _VirtualBox guest additions_. They enable the hypervisor to directly interact with the guest system enabling some features like a shared clipboard, enhanced graphics options, shared folder support (which is not available on FreeBSD at the present time however), etc. The downside is that they are quite large (due to their dependencies): ca. 350 MB. If you're aiming for a small base box, leave them out if you don't need them. Otherwise consider installing them.

In case you built VirtualBox on your host system from ports, you _might_ do the same for the guest additions so that versions match. Building them from ports does complicate things a bit however. You need the system source installed, it will use quite some space and take quite some time to fetch. Usually it's not worth the hassle. Unless you require some feature that only works when the versions match (I've never run into one), just install the guest additions using packages:

# pkg install virtualbox-ose-additions

Installing the vbox guest additions (PNG)

If you installed them, enable the guest additions for the next boot:

# sysrc vboxguest_enable="YES"
# sysrc vboxservice_enable="YES"

Preparing the VM for Vagrant: Users

The most important requirement for Vagrant is that a specific user has to be present. In FreeBSD you can create it all by issuing one line:

# echo vagrant | pw adduser -n vagrant -m -s /bin/tcsh -h 0 -L default -c "Vagrant user" -G wheel

This will make _pw_ add a user with the name vagrant to the system, create a home directory for it, set the shell (you can also choose another one here like /usr/local/bin/bash for example) and make it use what is piped into it as the password (that's a zero following the -h, btw.). It will also set the default login class for the user, use the provided user comment and add it to the wheel group (for which we've already configured sudo access).

Now that the vagrant user is present, Vagrant could log you into a VM that it has started from the base box. However it will ask for the password. This is just a small annoyance really, but we can do better. So let's prepare the vagrant user for automatic login (using SSH keys). To do so, we first switch to the new user:

# su -l vagrant

Next we create a _.ssh_ dot directory in user vagrant's home directory:

% mkdir .ssh

Then we download the public key from the Vagrant insecure keypair (it's insecure because the private key is public, too - Vagrant uses it to log in via SSH and it's open source software! But that's not a problem here) into the new directory e.g. with the following line:

% fetch --no-verify-peer https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub -o .ssh/authorized_keys

This downloads the _vagrant.pub_ file as _authorized_keys_ (the file that SSH checks against) and skips verifying the SSL certificate of the remote web server (this is necessary because FreeBSD does not ship with a collection of trusted root certificates and we haven't installed one).

The last thing to do is setting the correct permissions for the new directory and file (SSH is quite picky about that - and rightfully so):

% chmod 0700 .ssh
% chmod 0600 .ssh/authorized_keys

When that's done, we can log out as vagrant, back to the still logged in root:

% exit

With the key in place we have everything that's required for a simple FreeBSD 11 base box.

Customizing the base box

Now's the time to customize your base box. You could install additional software, add more users, do specific configuration, etc. I'm going to prepare the template for testing Bacula with multiple nodes. Since I need to build Bacula with non-standard options, I need to use the ports tree. So let's put that in _/usr/ports_ first:

# portsnap fetch extract

Then we change into the directory for the Bacula client:

# cd /usr/ports/sysutils/bacula-client

We configure the compile options for the Bacula client as well as for all its dependencies next:

# make config-recursive

I usually deactivate NLS (support for non-English translations), IPv6, DOCS and EXAMPLES for all programs. You may choose different options of course.

Since I need the Bacula client on every machine I can simply build and install it now:

# make install clean

Now let's change into the Bacula server ports directory:

# cd ../bacula-server

Again we need to set the build options for the port and dependencies:

# make config-recursive

Setting compile options for bacula server (PNG)

Deselect _postgresql_ and select _sqlite_ instead! When you accept the settings, you will be asked to select the settings for postgresql next. This is because the list of ports to configure was compiled before you changed the dependencies by choosing to build with support for another database. Hit CTRL+C to cancel the operation and then run _make config-recursive_ again.

Just like before, leave out unneeded options to keep the whole system a little lighter. Leave sqlite3 as-is if you don't know too much about it (it's a port with A LOT of options and can look rather daunting).

Then we fetch the source archives for Bacula server and its dependencies (this is done because I want to configure the VMs to be restricted to the local network later and thus they won't be able to connect to the internet then):

# make fetch-recursive

Finalizing the VM

If you're building a base box that you intend to share with others, it makes sense to try to cut down the size as far as possible. Here are a few suggestions:

# pkg clean -a
# rm -rf /var/db/freebsd-update/*

Just deleting stuff won't make your VM's virtual hard drive smaller during packaging, though! If you're using a traditional filesystem (i.e. UFS) there's an easy trick to "reclaim" the now unused space: Zero it out! Don't try to do this if you're using ZFS - you won't be able to fill your drive with zeros if the filesystem compresses them away! So for UFS filesystems do the following:

# dd if=/dev/zero of=/ZERO bs=1m

This will create the file ZERO in / which will be filled with all zeros until the filesystem runs out of space. If you delete that file afterwards, the part of the filesystem that is free again is all zeroed out and thus compresses well when packaging the base box:

# rm /ZERO

Zeroing out free space (PNG)

If you chose to use multiple filesystems, you may want to dd to a ZERO file on each separate filesystem (e.g. /usr/ZERO, /var/ZERO, etc.).

Finally everything is ready, so shutdown the VM now:

# shutdown -p now

Building the base box

Wait for the VM to power off. Now open a terminal emulator on your host system and tell Vagrant to build a base box from the VM:

% vagrant package --base fbsd-template -o fbsd-template.box

The argument after --base is the name of the VM as it appears in VirtualBox. Mind upper and lower case! Choose any name for the output file (the extension is usually .box). This will take a moment but if all goes well you should eventually have the base box file!

% vagrant box list

This lists all Vagrant boxes available on your system. If you've just installed Vagrant before it will tell you that it doesn't have any boxes yet. So let's import our newly built box:

% vagrant box add --name fbsd-template fbsd-template.box

Give it any name you want the base box to be known as by Vagrant and point the program to the .box file. If you do

% vagrant box list

again, you should see that it's available now. What's left? Let's test if it actually works.

Base box created and added! (PNG)

Testing the base box

To do so, I suggest creating a directory for your Vagrant boxes:

% mkdir -p ~/vagrant/testvm
% cd ~/vagrant/testvm

In that directory issue the following command:

% vagrant init

This creates a default _Vagrantfile_. Think of this as the central configuration for a single VM that Vagrant manages. It contains the name of the base box to create it from, allows configuring of the network, synced folders, overriding base box settings (like the amount of RAM for the VM), provisioning, etc.

Edit this file, have a look at the contents (you can also do this at a later time - _vagrant init_ will always create this default Vagrantfile for you if you need it) and delete it. Put the following lines in the Vagrantfile:

Vagrant.configure("2") do |config|
config.vm.box = "fbsd-template"
config.vm.synced_folder ".", "/vagrant", disabled: true
end

Substitute _fbsd-template_ with the name of your base box if you chose a different one. The second configuration directive disables synced folders: They don't work for FreeBSD guests at the present time and Vagrant would issue an unnecessary warning if we didn't disable it. This is all you need for a very basic configuration. Save the file and exit your editor.

Now run:

% vagrant up

This command tells Vagrant to create a new VM from the base box (VM template) selected in the Vagrantfile and fire it up. Vagrant will do so in the background - it runs the VM in so-called _headless mode_.

VM created from base box has booted up (PNG)

You don't need the VirtualBox GUI for Vagrant, but right now take a look at it. You'll see a new VM that was created by Vagrant and in fact you could also use VirtualBox to control it. But that's rarely necessary. Vagrant is meant to manage boxes for you completely after all!

To enter the VM, you can execute:

% vagrant ssh

Now you're SSH'ed into the VM! It's as easy as that. Issue an _ls_ or something to see that it's working. Exit the machine by logging out (type _exit_ or press _CTRL-D_) and you're back in your host machine's VM directory. Try _vagrant ssh_ once more and you see that you're inside the VM again.

SSH'ed into the box using Vagrant (PNG)

Cleaning up

Shutdown the VM now by running the following command line in the VM:

% sudo shutdown -p now

When the VM shuts down the SSH daemon, you're automatically dropped back to your host system. Let's quickly do some cleaning up and we're done for now:

% vagrant destroy

This will purge the VM. If you keep the Vagrantfile, you can just _vagrant up_ afterwards to start over with a clean VM (from the base box) again. This is great for testing things. You can also control the VM from outside using commands like _vagrant halt_, _vagrant suspend_, _vagrant resume_, etc. I suggest you take a look yourself and learn what Vagrant can do for you. It really has some neat and useful features.

Just a

% cd .. && rm -r testvm

later we're done with cleaning up after ourselves. The base box works and can be used to quickly spin up VMs based off of it. While this is neat, the real power Vagrant has and the real comfort it provides show when you're working with several VMs at the same time. Which is what I intend to do in the post after the next one.

What's next?

The next post will be an introduction to the Bacula backup software, exploring the basic functionality.

BACK TO 2016 OVERVIEW