A few months back I finally hit a point where maintaining my local development environment was taking up too significant a portion of the day. Tweaking things to make the environment suitable for one project would break something for another. This was leading to switching between projects becoming somewhat of a nightmare. The time expense of switching between projects had become too great.

Enter Vagrant. Vagrant is a command-line management tool for a virtual provided. In my case I’m using Vagrant to provision instances of virtual machines in VirtualBox on my Mac OS workstation.

Vagrant lets me develop in an Ubuntu-based environment that closely matches the staging and production environments. Especially true in Rails, this is helpful to avoid surprises that pop up when switching from Mac OS on development to Linux on staging and production.

It’s great for team projects where development environments and platforms really vary. One person is on a Mac, another on a PC, but they’re brought to a common development setup trough Vagrant.

When I start a project I use this boilerplate Vagrantfile. It has some helpful snippets that I’ve picked up along the way and is tailored to be quick to implement.

Note that my boilerplate is specific to Mac OS.

hostname = "vagrant"
project  = "project"
box      = "ubuntu/trusty64"
cpus     = `sysctl -n hw.ncpu`.to_i
memory   = `sysctl -n hw.memsize`.to_i / 1024 / 1024 / 4
recipes  = %w{essentials permissions}

Vagrant.configure(2) do |config|
  config.vm.box = box
  config.vm.hostname = hostname

  config.vm.network :private_network, ip: "10.10.10.2"
  config.vm.network :forwarded_port, guest: 3000, host: 3000

  config.vm.provider :virtualbox do |vb|
    vb.customize ["modifyvm", :id, "--cpus", cpus]
    vb.customize ["modifyvm", :id, "--memory", memory]
    vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
  end

  config.vm.synced_folder ".", "/home/vagrant/#{project}", nfs: true

  config.vm.provision "chef_solo" do |chef|
    chef.node_name = hostname
    chef.cookbooks_path = "chef_solo/cookbooks"
    chef.data_bags_path = "chef_solo/data_bags"
    recipes.each { |r| chef.add_recipe r }
  end
  config.vm.provision "shell", inline: "sudo usermod -a -G adm vagrant"
  config.vm.provision "shell",
    inline: "echo cd /home/vagrant/#{project} > /home/vagrant/.default_dir",
    run: "always"
  config.vm.provision "shell",
    inline: "grep -q -F 'source \"/home/vagrant/.default_dir\"' /home/vagrant/.bashrc || echo 'source /home/vagrant/.default_dir' >> /home/vagrant/.bashrc",
    run: "always"
  config.vm.provision "shell",
    inline: "grep -q -F 'source \"/home/vagrant/.default_dir\"' /home/vagrant/.zshrc || echo 'source /home/vagrant/.default_dir' >> /home/vagrant/.zshrc",
    run: "always"
end