A fresh take on Vagrantfiles

04 May 2016 . general

Application development problems often boil down to platform configuration issues. When you’re working in teams, this is even more the case. Differences in software versions and configuration between team members, and development environments (dev/uat/production) are often hard-to-tackle issues.

Automated virtualization tools like Vagrant are (a part of) the solution for a lot of teams, especially when coupled with an automated provisioning tool like Puppet or Ansible.

However, when you have lots of teams working on lots of different projects, requiring many different configurations, managing Vagrantfiles becomes the new hurdle. This often results in the one-Vagrantfile-per-project strategy, which leads to endless copy-pasting.

Instead of managing different Vagrantfiles and configuration code bases per project (which often tend to have hard-coded parameters), why not apply the best practices for programming and separate data and code? By putting all configuration parameters in a config.yaml file, your Vagrantfile can be shared between projects without issue.

One of our customers needed a way for their developers to spin up boxes, without doing any manual configuration. Just run vagrant up, and let vagrant handle everything, asking for details about the project if needed. To that end, we came up with following structure for our config YAML:

  custom_setting: value
  other_custom_setting: other_value
  setting: value
  other_setting: other_value
  timestamp: value
  hostname: value
  custom_setting: default_value
  other_custom_setting: [option1, option2, option3]
  custom_setting: ^[a-z]+$

Under the custom hash, all settings that pertain to this project are kept. This includes, most importantly, the box type, which is used to determine which software the provisioner (in this case Puppet) needs to deploy. If any of these settings are empty or invalid (based on the values in the regex hash), Vagrant will ask developers for their values, providing default values, or lists of accepted values if these are defined in defaults.

The static section holds all settings that usually don’t need changing. For example, the amount of RAM a box gets, or the IP address it needs to be assigned. This only needs to be changed in very specific circumstances and should be filled in beforehand by whoever is creating this Vagratfile in the first place.

The timestamp and hostname (which includes the timestamp) in the static section are needed because Puppet requires unique hostnames for its SSL certs. This will ensure that each box gets provisioned correctly and without asking questions (make sure you use autosigning).

Using this scheme allows us to ship a single Vagrant file for all projects. We can switch the provisioned software (and configuration), switch operating systems, or even switch privisioners entirely without requiring code changes. The initial setup took some work, but the advantages are obvious and well worth it.

This gives us a very easy to use and flexible solution for our developers, which is also easily managed by our sysadmins. Just vagrant up, and five minutes later you’re good to go.

Share this blogpost