14 Comments

Developing on OS X Inside Vagrant

OS X El Capitan installing in VirtualBox via Packer

I maintain an OS X tool for sandboxing the popular Homebrew package manager called brewdo. Because I need my MacBook to keep working day-to-day, I need a way to develop features and fixes in brewdo without risking my system’s integrity.

If brewdo targeted Linux, I could very easily use Vagrant to spin up a Linux system and develop it there. brewdo targets OS X, though. And although Apple gives permission to virtualize it, they don’t give permission to share copies of it.

But all is not lost. OS X on Vagrant is achievable, and without skirting Apple’s copyright.

1. Getting Started

I use (and have contributed VirtualBox support to) Tim Sutton‘s excellent osx-vm-templates project to build Vagrant boxes for OS X.

Tim recommends using VMware Fusion with the VMware Fusion provider. I personally use Vagrant’s built-in VirtualBox support, because it’s much cheaper (i.e. free) and brewdo is my only need for virtualized OS X. VirtualBox is serviceable, but not a great performer. Parallels is also an option.

You’ll also need Packer to build the boxes, and an installer .app from the Mac App Store for the version of OS X you want to Vagrantize. The rest of what you need (apart from Vagrant and your virtualization solution) is either in the osx-vm-templates repo or already on your Mac.

2. Building the Disk Image

The first thing you’ll need to do is prepare an installer disk image from the .app your downloaded from the Mac App Store. This is accomplished using the prepare_iso tool from osx-vm-templates. Give it the installer .app and an output directory, and it’ll give you an installer disk image ready for Packer:

$ sudo prepare_iso/prepare_iso.sh \
    /Applications/Install\ OS\ X\ El\ Capitan.app/ \
    packer/

Note that as of this writing, you need to do one additional thing if you’re a VirtualBox user: stop prepare_iso from enabling the Remote Management service, which is used with Apple Remote Desktop. If you leave it enabled, the resulting virtual machine will periodically freeze. This appears to be a VirtualBox bug.

Thankfully, disabling it is a piece of cake:

$ sudo prepare_iso/prepare_iso.sh \
    -D DISABLE_REMOTE_MANAGEMENT \
    /Applications/Install\ OS\ X\ El\ Capitan.app/ \
    packer/

When prepare_iso is done, take note of the output at the end—it’ll tell you what the filename of your new disk image is, which you’ll need for the next step.

3. Packing the Vagrant Box

The rest of the process is driven by Packer and is really easy. Here’s the command line I’m using for my latest build, executed from the packer directory:

$ packer build \
    --var iso_url=OSX_InstallESD_10.11.1_15B42.dmg \
    --only virtualbox-iso \
    template.json

Let’s break that down:

  • iso_url is the path to the installer disk image that prepare_iso built for us.
  • --only virtualbox-iso tells Packer to build only using VirtualBox. I actually have VMware Fusion on my machine, but not the Vagrant provider for it, so I don’t want Packer to build a box for VMware. Though it’ll happily do that if I want.

This process is quite time-consuming, but you can watch progress in the virtual machine console that pops up and on the command-line. Once done, you’ll have a Vagrant box.

4. Using the Box with Vagrant

Packer’s output is a Vagrant box, e.g. packer_virtualbox-iso_virtualbox.box. Adding that box to Vagrant is straightforward:

$ vagrant box add \
    --name osx-10.11.1 \
    packer_virtualbox-iso_virtualbox.box

Once the box is in, you can head over to your project directory and run vagrant init osx-10.11.1 (or whatever you named your box) to set up the initial Vagrantfile.

You’re almost ready to vagrant up, but one final caveat applies for VirtualBox users—there are no guest additions for OS X, so if you want to sync files, you’ll need to set up a shared folder in the VagrantFile to use rsync:

config.vm.provider "virtualbox" do |vb|
  config.vm.synced_folder ".", "/vagrant", type: "rsync"
end

And you’re done! vagrant up, vagrant ssh (or use some other remote access tool, like Screen Sharing), and away you go!