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!
This guide is great! I was looking for a solution to develop iOS in a repeatable manner, this seems to be an answer!
Following this guide, however, the checksum didn’t go through with the name iso_checksum. I checked the template.json from the git repo, and that seems to have changed to iso_checksum_type. You may want to update this article for future devs with the same need.
Cheers!
Thanks for the heads-up! I know there have been some changes lately in the osx-vm-templates project disabling checksums for the image. I haven’t had a chance to look at them yet, but I’ll check it out when I can and update the post.
From what I gather, the linked PR completely removes the need for checksums.
My 2 cents. :D
Thanks! I confirmed, you don’t need a checksum to build the image anymore. I’ve updated the post.
Hi,
Thanks so much for this post really helpful. I have run into an issue at stage 3 and i get the following output from packer.
==> virtualbox-iso: Starting the virtual machine…
==> virtualbox-iso: Error starting VM: VBoxManage error:
==> virtualbox-iso: Unregistering and deleting virtual machine…
==> virtualbox-iso: Deleting output directory…
Build ‘virtualbox-iso’ errored: Error starting VM: VBoxManage error:
Any ideas where the log files will be to start diagnostics. I am trying to get El Captain running on a MBP.
Cheers
Dave
Hi David,
I’m not sure—I’ve never seen that before. If VirtualBox is installed correctly, it might be a Packer problem. I’d check to see if your Packer installation can build any other kinds of Vagrant boxes.
If you get an error about “VBoxManage error: VBoxManage: error: Implementation of the USB 2.0 controller not found!”
You’ll need to install the extension pack. See (https://github.com/timsutton/osx-vm-templates#extension-pack) for more info
I think its also worth adding that the entire process can take 3+ hours and you’ll need at least 25gb of free space (6+ for the El Capitan Installer, 9 for preparing the iso and another 9 for the packer build)
I had better luck using NFS for the shared folder, rather than rsync.
A private network is required for NFS to work, so if you want to use NFS for the synced folder, use something like this:
config.vm.provider “virtualbox” do |vb|
config.vm.network “private_network”, ip: “192.168.33.10”
config.vm.synced_folder “.”, “/vagrant”, type: “nfs”
end
Also worth noting, you may have to enter your admin credentials to get the NFS service running.
Yep, that’s a good option to have, especially if you’re used to live updates between the host and guest.
I personally prefer rsync because it’s lower-impact and sufficient for my needs, but definitely go this route if you find it useful.
I am getting this error Error creating VM: VBoxManage error: VBoxManage: error: Guest OS type ‘MacOS1012_64’ is invalid
Any idea what is the guest os type to use?
It doesn’t appear that VirtualBox has specific support for macOS Sierra at this time, so you would want to try MacOS1011_64 instead.
Okay. Thanks! Will change and try. Thanks
This is the complete error message :
==> virtualbox-iso: Error creating VM: VBoxManage error: VBoxManage: error: Guest OS type ‘MacOS1012_64’ is invalid
==> virtualbox-iso: VBoxManage: error: Details: code VBOX_E_OBJECT_NOT_FOUND (0x80bb0001), component VirtualBox, interface IVirtualBox, callee nsISupports
==> virtualbox-iso: VBoxManage: error: Context: “CreateMachine(bstrSettingsFile.raw(), bstrName.raw(), ComSafeArrayAsInParam(groups), bstrOsTypeId.raw(), createFlags.raw(), machine.asOutParam())” at line 275 of file VBoxManageMisc.cpp
==> virtualbox-iso: Deleting output directory…
Build ‘virtualbox-iso’ errored: Error creating VM: VBoxManage error: VBoxManage: error: Guest OS type ‘MacOS1012_64’ is invalid
VBoxManage: error: Details: code VBOX_E_OBJECT_NOT_FOUND (0x80bb0001), component VirtualBox, interface IVirtualBox, callee nsISupports
VBoxManage: error: Context: “CreateMachine(bstrSettingsFile.raw(), bstrName.raw(), ComSafeArrayAsInParam(groups), bstrOsTypeId.raw(), createFlags.raw(), machine.asOutParam())” at line 275 of file VBoxManageMisc.cpp