Making Vagrant+VirtualBox+NFS Less Painful With Sparse Disk Images.

When using Vagrant+VirtualBox on OSX with a Linux guest, sometimes configuring an NFS mounted share is useful... until you run into case-sensitivity issues between your host and guest. It's a subtle issue that isn't a problem all the time, but when it does happen, fortunately, there is a fairly simple solution.

The Problem

When using vagrant with virtualbox to manage linux guests, a 2-way sync between linux guest and OSX host is sometimes desired. Occasionally this can lead to issues that arise due to the difference in the case sensitivity of each file system.

Possible Fixes

There are really only a few options to work around this issue:

  1. Use rsync (but that's only a 1-way sync)
  2. Use VirtualBox shared folders (but it's slow... suitable for just transferring files, but not practical for anything that needs much less latency)
  3. Use [vagrant-]sshfs (still slow, but better than VirtualBox shared folders, but requires software to be installed on the client system and a vagrant plugin)
  4. Use [vagrant-]unison (haven't tried it, but it reports to solve the issue; however, it also requires a vagrant plugin and software installed on the guest)
  5. Edit everything on the guest... which means either using a terminal based editor (IE vim, emacs, etc) or using a host-installed editor that knows how to edit files remotely (which might still require software installed on the guest).
  6. Reformat your OSX drive with a case-sensitive filesystem (in addition to needing to resetup your entire machine, you might also run into conflicts with applications that expect the case-insensitive default).

All these can fix the problem to some degree or another, but they leave a lot to be desired. Requiring only 1-way sync, additional client or host software requirements, and speed plague these options.

Solution: Sparse Disk Images

The BEST solution would involve not having to do anything special to begin with. Aside from that, sparse disk images seem to be the next best thing.

Disk images on OSX are a type of file that is effectively a self-contained virtual disk volume that is addressable like a file, but mountable like a hard disk of some sort. ".dmg" files are disk images that you see quite often in OSX.

Sparse disk images are a kind of disk image that grow with use -- IE if 100GB are allocated to a sparse disk image, it may only take up a few hundred MB on the filesystem, until more information is stored on it.

These disk images are able to be created with utilities installed by default on OSX and are able to be formatted in a case-sensitive filesystem. They are mountable as an NFS share (the fastest sync/sharing option in Vagrant/VirtualBox) into guest machines, and are very accessible on the host.

The utility that makes scripting the creation and mounting of sparse disk images possible is [hdiutil](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/hdiutil.1.html).

A command like this will create a 10GB (maximum) sparse disk image with the case sensitive version of HFS+:

hdiutil create /path/to/image.dmg -type SPARSE -fs "Case-sensitive Journaled HFS+" -size 10g -volname "Name of Volume"

And then a command like this will mount the image to a path in the `/Volumes/` directory:

hdiutil attach /math/to/image.dmg

Using this in a Vagrantfile becomes pretty simple since the Vagrant file is just Ruby! Every time `vagrant up` is run, the idea is to:

  1. check to make sure the disk image exists (and create it if it doesn't)
  2. mount the image, if it isn't mounted already
  3. create a symlink to the volume in the working directory of the Vagrantfile
  4. mount the volume into the guest with NFS
  5. disable the default /vagrant share (no need for it with the NFS share)

Here's some Ruby that you can put into the Vagrantfile to accomplish this:

# only on `vagrant up` commands
if ARGV[0] == "up" then
    # just convenience for not retyping long paths
    workspacepath = "/path/to/vagrantfile_containging_folder/workspace"
    # 1.
    if not File.exists?("/path/to/image.dmg")
        system 'hdiutil create "/path/to/image.dmg" -type SPARSE -fs "Case-sensitive Journaled HFS+" -size 10g -volname "Name of Volume"'
    end
    # 2.
    system 'hdiutil attach "/path/to/image.dmg"'
    # 3.
    system 'ln -s "/Volumes/Name of Volume" "'+workspacepath+'"' unless File.exists?(workspacepath)
    # 4.
    server.vm.synced_folder workspacepath, /path/on/guest/to/mount/to, nfs: true
    # 5.
    server.vm.synced_folder ".", "/vagrant", disabled: true
end

While this is not the ideal solution (doing nothing but enabling an NFS share in the Vagrantfile would be the 'ideal'), it IS scriptable, and it doesn't require any special installation of software.

A side effect is having code/files/etc. in a disk image that could be transferred around independently, which may prove useful!