Easy Kernel Upgrades in a Fedora 23 VirtualBox Guest

I love Vagrant. It’s a great tool for configuring and deploying identical development environments across different computers and platforms. I’m currently toying around with the concept of using a virtual machine (deployed using Vagrant, of course) as my main work environment, and just leaving the host OS as the host. I also want to get more familiar with Red Hat (Ubuntu has been my distro of choice in the past), so I started building a new Vagrant box on Fedora 23. The problem is that Fedora’s kernel upgrades don’t play nicely with the VirtualBox Guest Additions, but I found a solution.

The Problem

It’s pretty simple to recreate the issue:

  1. Create a virtual machine with Vagrant, using the Boxcutter Fedora 23 box (version 3.0.1 specifically).
  2. Run dnf upgrade on the VM.
  3. Restart the machine (e.g. with vagrant reload). Vagrant can no longer mount the shared directories in VirtualBox, which means any additional provisioning is dead in the water.1

A Solution

There may be a better way to solve this issue. There are almost certainly other ways. This is the one that I found. The basic approach follows:

  1. Copy the Guest Additions installer to the Fedora VM.
  2. Install DKMS, developer tools, and the correct version of the kernel sources on the VM.
  3. Re-install the Guest Additions.
  4. Update the kernel (and other software packages).

The great thing about doing this in Vagrant is that these steps are super simple to automate using Chef Solo. Doing them in the correct order is critical, but lucky for you, I’ve already done the trial-and-error to get that nailed down.

Before We Begin

I’m going to assume that you already have VirtualBox, Chef DK, and Vagrant installed on your host. This was written using VirtualBox 5.0.10 and version 3.0.1 of the Boxcutter Fedora 23 base box. I think my solution is relatively future-proof, but I’m not making any promises.

The other prerequisite is that you need to extract the VirtualBox Guest Additions for Linux from the ISO image, which is located in the VirtualBox directory. Specifically, the file is called VBoxLinuxAdditions.run, and I put it in the files/default directory of my Chef cookbook.

Here’s what my Vagrantfile looks like:

Vagrant.configure(2) do |config|
  config.vm.box = "box-cutter/fedora23"
  config.vm.box_check_update = false
  config.vm.hostname = "captain-marvel"
  config.vm.network "private_network", ip: "192.168.56.26"

  config.vm.provider "virtualbox" do |vb|
    vb.name = "Captain Marvel"
    vb.memory = "1024"
    # vb.gui = true
  end

  # Provision with Chef Solo
  config.vm.provision "chef_solo" do |chef|
    chef.add_recipe "danvers::system"           # configure the system
    chef.add_recipe "danvers::users"            # configure users
    chef.add_recipe "danvers::packages"         # install packages
  end
end

Nothing out of the ordinary here. I’m using Chef Solo to provision the VM. Specifically, the danvers::packages recipe handles the all of the steps.

Copy the Guest Additions installer to the Fedora VM

This resource is pretty straightforward.

cookbook_file 'guest additions' do
    path '/opt/VBoxLinuxAdditions.run'
    source 'VBoxLinuxAdditions.run'
    mode '0774'
    action :create
end

It looks in the files/default directory of the cookbook for the file we extracted from the Guest Additions ISO and copies it to the /opt directory on the guest. I prefer to copy the installer as the first step, so that it’s on the VM and you can run it manually if a later step craps out.

Install DKMS, developer tools, and the correct version of the kernel sources on the VM

This resource is slightly fancy.

script 'install dev tools' do
    interpreter "bash"
    code <<-EOH
        sudo dnf install -y dkms gcc-c++ make kernel-devel
        sudo dnf downgrade -y kernel-devel-4.2.3-300.fc23.x86_64
        sudo dnf downgrade -y kernel-headers-4.2.3-300.fc23.x86_64
        EOH
    action :run
    only_if { platform_family?('fedora') }
    not_if { ::File.exists?( '/usr/bin/gcc' ) }
end

The kernel on the Fedora 23 base is version 4.2.3-300.fc23.x86_64, but installing gcc-c++ and kernel-devel installs the latest version of kernel-headers and kernel-devel respectively. Thus, we need to downgrade to the version that matches the running kernel.

Note that, at the time of writing, running dnf downgrade without specifying the kernel version will still get you the right versions. I can easily foresee the day when that doesn’t work. Thus, I included the versions as a hedge against future updates.

Re-install the Guest Additions

This step actually requires two resources:

execute 'install guest additions' do
    command '/opt/VBoxLinuxAdditions.run'
    returns [ 0, 1 ]
    action :run
    not_if { ::File.exists?( '/opt/vbox-guest-additions-guard' ) }
end

file 'guest re-install guard' do
    path '/opt/vbox-guest-additions-guard'
    action :touch
    not_if { ::File.exists?( '/opt/vbox-guest-additions-guard' ) }
end

The first resource, the execute block, runs the Guest Additions installer, which will return 1 if it succeeds but does not install the X-Windows components. We have to tell Chef that this is not a failure state,2 so it will continue.

The file resource just creates an empty file. On future Chef runs, the resources will check for this file, see it, and not run, thanks to the not_if guards. We can’t check the presence of particular files, because the Guest Additions are already installed on the base box–we’re just re-installing. If there’s a more elegant solution, please let me know!

The bottom line here is that Chef should only run the installer once for a given VM. If you need to run it again–hopefully, you shouldn’t after this, but if you do, you’ll have to do it manually.

Update the kernel (and other software packages)

Easy-peasy:

execute 'upgrade packages' do
    command 'sudo dnf upgrade -y'
    action :run
    only_if { platform_family?('fedora') }
end

Wrapping Up

I put all of the resources into a gist for your easy copying-and-pasting needs.

Like a lot of things, the solution seems pretty simple when presented, but getting there wasn’t the easiest thing. I struggled for a long time to figure out why the Guest Additions kept breaking. Once I had identified the problem, the solution wasn’t self-evident. I experimented with re-installing the Additions from an RPM repository, so I didn’t see how to script installing via a virtual CD-ROM in the VM, which is the traditional manual approach. The real breakthrough was figuring out that I could make the installed directly available to the VM with Vagrant and Chef. Then, it was just a matter of figuring out the correct order for doing everything.

On my laptop, it takes between 5 and 7 minutes to go from vagrant up to a fully provisioned VM.


  1. The root cause of the problem is moderately technical. In the Boxcutter base box, the VirtualBox Guest Additions are installed without DKMS. When dnf upgrade is run, it upgrades the kernel. This results in the Guest Additions being out of sync (for want of a better term) with the kernel. [return]
  2. We could use the ignore-failure :true switch here, but then that would suppress actual failures. [return]

Comments