Building a Custom Drupal Image for Vagrant

Dec 6 2011

Not too long ago, I posted a blog entry about 5 reasons for using a virtual machine for Drupal development. At the suggestion of some commentors, I have started looking into using Vagrant to manage my VMs. There is an excellent Drupal Vagrant project that provides a great starting point. This article explains how to begin with that and modify it to suit your own needs.

This isn't a detailed explanation, but rather a high-level roadmap to get you started developing your own site on Vagrant/VirtualBox. There are only a few short code samples, and most of the customization is left up to you. But as you'll see, Vagrant is designed to be easily modified. Once you've got an understanding of the process, the rest is (as they say) an implementation detail.

The Goal

We want to...

  • Continue doing development on the local box with our normal tools.
  • Have a copy of the site running inside of a virtual machine.
  • Be able to test in real time. Make a change, save a file, have it show up on the VM.
  • Still allow version control tools to function normally.

Prerequisites

Before beginning, you should install VirtualBox and Vagrant, and grab a copy of the Drupal Vagrant project. (WARNING: I tried this on 64-bit Windows 7 and it does not work because of bugs in JRuby. AFAIK, it works on all other supported platforms.)

Follow the instructions (README.markedown) to install and create a Drupal Vagrant box. Make sure it works. Once you are sure, type vagrant destroy to destroy your VM (this will stop the server and then delete it).

For this article, I will assume that your Vagrant instance is in vagrant/

Warning (and consolation): Vagrant and its deployment system (Chef) are written in Ruby. Many of the files you will encounter are actually Ruby source code. But don't worry, you don't really need to know Ruby to do this stuff. The high-level libraries that Vagrant and Chef provide make the files feel more like configuration files than scripts.

Step 1: Create a New Role

In vagrant/roles, there are a handful of ruby files that define "roles". In a nutshell, rolls are sorta like installation profiles. They allow you to tell Vagrant what sort of system you want it to build (what role it will play).

Take a look at the role called drupal_lamp_varnish_dev.rb. That is the default role for Drupal Vagrant. It's a good place to start, so copy that into myrole.rb.

This file gives Vagrant a list of roles and recipes that will be run.

name "lamp_varnish_drupal_dev" # Change this to 'myrole'
description "A LAMP + Varnish + Memcached stack for Drupal similar to Mercury
with development tools."
run_list(
  "role[apache2_mod_php]",
  "role[apache2_backend]",
  "role[drupal]",
  "role[drupal_dev]",
  "role[memcached]",
  "role[mysql_server]",
  "role[varnish_frontend]",
  "recipe[drupal::drupal_apps]"
)

There are things to note about this:

  • Roles can run other roles. In that way, you can chain together different roles to build new profiles.
  • Recipes are particular instructions for doing something. More basic roles have run_lists mostly comprised for recipe calls.

This will be your basic role. You will add and remove things from it as necessary.

Right now, all you need to do is change the name to 'myrole' and save it.

Step 2: Update your Vagrantfile to use the new role.

In /vagrant, there is a file called Vagrantfile. This is the master build profile, and it is the first thing Vagrant reads when building a new machine.

Edit it and look for a line that says chef.add_role('lamp_varnish_drupal_dev'). Change it to load your new role: chef.add_role('myrole').

Now the next time you do a vagrant up, it will determine what kind of system to build by consulting myrole.rb.

Step 3: Create a cookbook

In this configuration of Vagrant, we are using Chef. Chef is a build system that uses cookbooks -- collections of recipes -- to deploy a system.

In our case, we will be creating various recipes, and we need a place to put those.

So create the directory vagrant/cookbooks/custom-cookbooks. You can store your own cookbooks in here. Let's create one called mysite:

$ cd vagrant/cookbooks/custom-cookbooks
$ mkdir -p mysite/templates/default mysite/recipes

That will create a new cookbook called mysite with a couple of directories in it: templates and recipes. The templates directory is for template files. The recipes directory is for recipe scripts.

Step 4: Create a recipe

Recipes in Chef/Vagrant are short Ruby scripts written in Chef's domain language. If that sounds daunting, don't worry. The fact of the matter is that in most cases, you can learn just a few patterns and apply them with great success.

By far, the best place to start is with the vagrant/cookbooks/drupal-cookbooks/drupal/recipes/example.rb recipe. This builds out a new Drupal site from scratch, configuring the database and using Drush Make to build a pristine site.

Copy the example.rb file into your own mysite cookbook and start working on it. You also should add this line to your myrole role at the end of the run list: recipe[mysite::example.rb]. (Optionally, you may want to remove the line chef.add_recipe('drupal::example') from the Vagrantfile. By default, Drupal Vagrant loads its own example recipe.)

Take some time to look at the example.rb file, and play around with it. Use vagrant up to create a new server. In most cases, instead of destroying and rebuilding, you can update a site to the latest file changes you've made simply by doing vagrant provision. And you can ssh to your site doing vagrant ssh.

(One noteworthy section of example.rb is how it executes SQL commands. You can tweak that section to load a dumped DB file, should you so desire.)

Step 5: Linking your local (host machine) Drupal installation with your Vagrant box.

There are many things you will likely want to tweak and tune in Vagrant, and an entire book could be written on the topic. But here is one thing you are quite likely to want to do: Link your local dev environment to your Vagrant box.

Doing this is a two-step process:

First, in the Vagrantfile, you should add this:

config.vm.share_folder("my_site", "/var/www/my_site", "/full/path/on/your/local/dev/drupal")

This configures your VM to link its /var/www/my_site folder to the local directory you supply.

Second, you need to tell Apache to use your site as a docroot. There are a few ways of doing this, but I find the most effective to be copying the vagrant/drupal-cookbooks/drupal/recipes/drupal_apps.rb recipe and the vagrant/drupal-cookbooks/drupal/templates/default/sites.conf.erb templates into their respective locations underneath your new cookbook, and then altering the docroot value in drupal_apps.rb. The docroot should point to var/www/my_site. (Basically, your build an Apache virtual host file with this template.)

Once you've done that, edit your myrole.rb role file, and change it from running drupal::drupal_apps to mysite::drupal_apps.

Wrapping Up

At this point, you should have a stable virtual machine whose doc root is synced with your local development environment. You should be able to continue using your chosen IDE (though you might need to configure the debugger), git or svn, and whatever other development tools you use.

There is much, much more that can be done with Vagrant. Here we've barely scratched the surface. But I hope this helps get you started.

Reading the included recipes will help you make that next step. Here are a few more resources that you should read or use as reference:

Updated: Added a link to the original 5 reasons post.