> Martin Polden

Reproducible development environments using Vagrant

Meta: Originally published by me on the internal Statoil developer blog.

The complexity of any given development environment often grows at the same rate as the project itself.

A large project can have countless dependencies such as a web server, a database, a caching system, a queue and more. This amounts to a lot of configuration and administration overhead. Suddenly, adding a new developer to the project means spending days just installing build tools, services, libraries and other dependencies before he/she can write any code.

Each developer might also be running different operating systems and using different tools, some might run Windows, others Linux or OS X. This adds to the complexity as some dependencies might have subtle platform-specific differences and/or bugs. Developing on an OS that differs from the production OS can also lead to subtle bugs when the application is deployed.

Vagrant

Vagrant attempts to solve this problem by creating reproducible development environments using virtual machines. Vagrant creates a virtual machine from a base image and provisions it using either a basic shell script or a configuration management tool.

When using Vagrant each developer will have an identical and isolated environment which is configured with the necessary dependencies. This reduces the setup overhead of the project and prevents "works on my machine" situations.

Developers can still use the editor and tools of their choice and won't have to sacrifice anything (editor and tools such as Git still run on the host machine).

If you already have a configuration management tool in your current production environment, you can reuse them to configure your Vagrant box. Or if you're starting out with configuration management tools through Vagrant, you can reuse what you learn when deploying the application in production. It's also possible to combine Vagrant with Jenkins using the Vagrant plugin, and have Jenkins use Vagrant when running jobs.

Configuration

Vagrant is configured by creating a Vagrantfile in your project root. Vagrant reads this file when managing the virtual machine for your project.

Vagrant mounts the project workspace inside the virtual machine, so the actual project files remain on the host machine, but are made available in /vagrant inside the VM.

The Vagrantfile describes the steps necessary to configure your development environment, such as the base image to use, which ports to forward between host and guest, the amount of memory for the VM and how it should be provisioned.

The convention is to version this file along with the other files in your project. You can think of it as just another project-dependent file, like a Makefile, pom.xml or requirements.txt.

Provisioning

The provisioning of the virtual machine is usually done with a configuration management tool (can also be a shell script for simpler projects). Provisioning consists of several steps such as installing required packages, copying configuration files or building configuration files from templates, ensuring services are running and more. A configuration management tool automates these steps.

How to use configuration management tools is beyond the scope of this blog post, but Vagrant comes with support for Puppet, Chef, Ansible or Salt all of which are very well documented.

Summary

Vagrant aims to reduce the complexity of working on any given project by eliminating cumbersome configuration steps and simplifying the workflow. Having used Vagrant on several projects it delivers on its promise, and it does make for a more fluid and less error-prone workflow, especially for complex projects with lots of dependencies.

Besides, developers are always more happy writing code than dealing with mundane tasks.

Automate all the things!

Vagrant

Vagrant documentation