Increased performance in Vagrant

May 15, 2012

I have been using Vagrant for a while now. Overall I am very satisfied with the simplicity of it and the way it can standardize environments when it comes to collaborating with others. In my bigger projects I usually setup a Vagrantfile.example with all the default settings that I commit to the git repository and then I add an exception for the Vagrantfile itself to allow for personal customization if that would be needed.

But one thing that bothered me more and more was the performance and mainly that of the Specs that I was running. At first I though that it was the lack of RAM in my laptop, or rather the increasing amount of programs and stuff I have running simultaneously. I have got into the habit of using the Guard gem to have my specs running while I keep coding so that I get notifications if any unexpected specs are suddenly failing. The thing I noticed with having guard running inside Vagrant though was that it took forever for guard to even notice the filesystem changes to realize that it should run certain specs again. And the longer it had been running, the more that delay increased.

WHAT TO DO ABOUT IT

So eventually I decided to see if I could do anything about it. And now I wish I had done it a long time ago. Because there is a configuration option that is mentioned in the documentation about NFS Shared Folders. I recommend everybody to check out if you like me have missed it until now.

To activate this configuration, you have to add :nfs => true to your shared folders inside your Vagrantfile. Now in some cases, there might not be a single usage of config.vm.share_folder in your Vagrantfile but that is only because the root folder of your project is always mapped even if you don't specify it. However you can override the root folders settings by naming your shared folder v-root.

So in my case where I have the root folder and a folder mapping to my database backup folder (for quick access to dump files), my configurations look like this:

config.vm.share_folder "v-root", "/vagrant", ".", :nfs => true
config.vm.share_folder "v-db-dump", "/vagrant_db_dump", "~/dumps", :nfs => true

Now there is another setting that you will have to change if you haven't already. And that is the :hostonly configuration for the network. If you don't then Vagrant will not allow you to use NFS shared folders. That setting look like this:

config.vm.network :hostonly, "33.33.33.10"

The IP address is the one you will use to access the virtual machine and can be set to whatever you want but I recommend that you leave it as the default if you are uncertain. And besides being necessary for this topic, I would recommend having it set to hostonly in any development environment for security reasons. As the description says, the virtual machine can not be accessed by any computer on your network except for the machine it is running on.

POTENTIAL PROBLEMS

One thing that stopped working for me once I had changed to these configurations was that I could not remotely access the MySQL server instance I had running on the virtual machine. And the reason for that was of course that the user was not allowed access from the IP address I was now connecting from.

Previously I was using port forwarding to access the mysql server which meant I was connecting to localhost. But now since we switched to a static IP address instead, that was not working anymore. In my case when I used 33.33.33.10 as the ip on my virtual server, the ip from which I am connecting FROM is 33.33.33.1. So the solution here was to grant my user to access from that ip address instead.

If you want more help on how to do that, please take a look at my cheat sheet.

RESULT

So to summarize this, I will show you what my Vagrantfile looked like after these configurations:

Vagrant::Config.run do |config|

  # Every Vagrant virtual environment requires a box to build off of.
  config.vm.box = "lucid32"
  config.vm.customize ["modifyvm", :id, "--memory", "512"]

  # Assign this VM to a host-only network IP, allowing you to access it
  # via the IP. Host-only networks can talk to the host machine as well as
  # any other machines on the same network, but cannot be accessed (through this
  # network interface) by any external networks.
  config.vm.network :hostonly, "33.33.33.10"

  # Share an additional folder to the guest VM. The first argument is
  # an identifier, the second is the path on the guest to mount the
  # folder, and the third is the path on the host to the actual folder.
  # config.vm.share_folder "v-data", "/vagrant_data", "../data"
  config.vm.share_folder "v-root",    "/vagrant",         ".",       :nfs => true
  config.vm.share_folder "v-db-dump", "/vagrant_db_dump", "~/dumps", :nfs => true

  # Enable provisioning with chef solo, specifying a cookbooks path (relative
  # to this Vagrantfile), and adding some recipes and/or roles.
  #
  # config.vm.provision :chef_solo do |chef|
  #   chef.cookbooks_path = "cookbooks"
  #   chef.add_recipe "mysql"
  #   chef.add_role "web"
  #
  #   # You may also specify custom JSON attributes:
  #   chef.json = { :mysql_password => "foo" }
  # end
  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "~/projects/cookbooks"
    chef.add_recipe "essentials"
    chef.add_recipe "mysql-install"
    chef.add_recipe "rbenv"
    chef.add_recipe "welcome-message"
    chef.add_recipe "imagemagick"
  end
end

Feel free to comment with any feedback or suggestions.