Ideally, we should all be developing our code in our own little space on our little local server. This allows us to easily make changes without messing up production code or stepping over other's work. This is usually cost prohibitive so we use virtual machines to make this a reality.

The problem we face is that each developer needs to have a virtual machine that is setup exactly (or nearly exactly) like our production server. This requires a long list of configuration changes that need to be made on every machine. For example, install the apache package, update this configuration file, setup Samba so you can access the files remotely. Then we run into more problems when additional changes are needed because the developer has to take time out of their schedule to make them on each machine. There are also passwords that have to be remembered and /etc/host changes that need to be made. You'll be in even worse shape if the deployment consists of multiple VMs.

Scott's Note: This is still a valid post but I suggest you read Getting Started With Vagrant Using PuPHPet.com for a slightly easy approach that doesn't require as much command line work and is easier (in my opinion) to work with.

Enter Vagrant

Vagrant is here to help us make this process easier. With Vagrant we can easily create a VM with all the packages and items we need in order to get our project up and running. It will then, just as easily, allow us to delete the virtual machine so we don't have it eating up space on our computer (with the cost of hard drives being what it is this argument might be completely academic).

The real benefit is that you will no be a slave to the "it works on their computer" excuse. This is a total cop out and really only hurts your internal and external customers. Because every development box has exactly the same configuration (or can in a matter of minutes) this extra variable is removed when it comes time to fix a bug.

On top of that Vagrant gives us some other really cool features. The best one in my opinion is that it automatically redirects your working directory to the /vagrant directory on the VM. That means you can use all your native applications (GitHub for Windows/Mac, Sublime Text 2, MySQL Workbench, etc.) and truly work the way you want to work.

Prerequisites

If you don't already have it installed install VirtualBox and Vagrant. I'll also be using a copy of Wordpress as an example or you can download all the code for this setup from this github repository https://github.com/warren5236/WordpressWithVagrant/tree/UsingBootstrap.sh.

Vagrant Init

The first step in a new project is to run vagrant init

vagrant init precise32 http://files.vagrantup.com/precise32.box

This will create a Vagrantfile in the current directory and configure it with the default settings to use a 32-bit Ubuntu Precise Pangolin (12.04). You can look at the Vagrantfile in any text editor but we're going to replace it with the following:

Vagrant.configure("2") do |config|
  config.vm.box = "precise32"
  config.vm.box_url = "http://files.vagrantup.com/precise32.box"
  config.vm.network :forwarded_port, guest: 80, host: 8080
  config.vm.provision :shell, :path => "bootstrap.sh"
end

It may look complicated but it's really not. The second and third lines specify the name of the VM and where to get it's base file. The fourth line forwards port 8080 on your machine to port 80 on the VM. The fifth line specifies a file named bootstrap.sh (also located in your root directory but feel free to move it else where) which will be run every time you boot the VM.

Our Bootstrap.sh

This script is going to contain all the commands that will setup our VM to be in the state we need it. This could all be done with Chef or Puppet but I don't think it's necessary for such a simple setup.

Installing MySQL, PHP and Apache

The first thing we want to do is install MySQL Server, PHP, and Apache so Wordpress has it's requirements. Luckily, Ubuntu has all of these in packages already so we can just install them directly. Unfortunately, the package for MySQL prompts you for a password and we want this to be a completely hands off experience. The first two lines in the commands below set the password for installing MySQL, the third line updates apt-get's list of packages (you can run into "missing" packages if you skip this step), and the fourth line installs everything we need.

sudo debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password password rootpass'
sudo debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password_again password rootpass'
sudo apt-get update
sudo apt-get -y install mysql-server-5.5 php5-mysql apache2 php5

Creating the database settings

The next section of the bootstrap.sh file looks like this:

if [ ! -f /var/log/databasesetup ];
then
    echo "CREATE USER 'wordpressuser'@'localhost' IDENTIFIED BY 'wordpresspass'" | mysql -uroot -prootpass
    echo "CREATE DATABASE wordpress" | mysql -uroot -prootpass
    echo "GRANT ALL ON wordpress.* TO 'wordpressuser'@'localhost'" | mysql -uroot -prootpass
    echo "flush privileges" | mysql -uroot -prootpass

    touch /var/log/databasesetup

    if [ -f /vagrant/data/initial.sql ];
    then
        mysql -uroot -prootpass wordpress < /vagrant/data/initial.sql
    fi
fi

Because this script will run every time the VM boots we need to make sure that the process for creating the database and the database user will only run once. To do this we touch a file name /var/log/databasesetup and if the file exists we skip over this part. The section at the very bottom checks to see if there is an initial data file and restores it to the database. I'll go over this more later in this article.

Configuring Apache

The last thing we need to do is to configure Apache:

if [ ! -h /var/www ]; 
then 

    rm -rf /var/www sudo 
    ln -s /vagrant/public /var/www

    a2enmod rewrite

    sed -i '/AllowOverride None/c AllowOverride All' /etc/apache2/sites-available/default

    service apache2 restart
fi

We're doing a similar trick to the one above but in this case we're checking to see if a symlink exists and then we're remapping the public directory in our repository to the /var/www directory (the default document root), enabling mod_rewrite (for permalinks) and then allowing the use of .htaccess files (also for permalinks).

The complete file

This is what the complete file should look like:

sudo debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password password rootpass' 
sudo debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password_again password rootpass' 
sudo apt-get update sudo apt-get -y install mysql-server-5.5 php5-mysql apache2 php5

if [ ! -f /var/log/databasesetup ];
then
    echo "CREATE USER 'wordpressuser'@'localhost' IDENTIFIED BY 'wordpresspass'" | mysql -uroot -prootpass
    echo "CREATE DATABASE wordpress" | mysql -uroot -prootpass
    echo "GRANT ALL ON wordpress.* TO 'wordpressuser'@'localhost'" | mysql -uroot -prootpass
    echo "flush privileges" | mysql -uroot -prootpass

    touch /var/log/databasesetup

    if [ -f /vagrant/data/initial.sql ];
    then
        mysql -uroot -prootpass wordpress < /vagrant/data/initial.sql
    fi
fi

if [ ! -h /var/www ];
then 
    rm -rf /var/www
    sudo ln -s /vagrant/public /var/www

    a2enmod rewrite

    sed -i '/AllowOverride None/c AllowOverride All' /etc/apache2/sites-available/default

    service apache2 restart
fi

'Install' Wordpress

Because we have the root MySQL password listed in the Vagrantfile we're going to want to protect it from prying eyes. The best way to do this is to keep it outside the directory that Apache is going to be serving. To do this we're going to create a public directory inside our working directory and then put all the Wordpress files there. If you looked closely in our bootstrap.sh file you'll notice that this is where we redirect /var/www to. When you're done it should look something like this:

Vagrant Up

Now it's time to create the VM. Type vagrant up into the command prompt in your project directory (the one with the Vagrantfile in it) and you'll get the following:

Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'precise32'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 =&gt; 2222 (adapter 1)
[default] -- 80 =&gt; 8080 (adapter 1)
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.

[Shortened so people don't go insane]

At the end of this you'll be brought back to the command prompt and if you attempt to access http://localhost:8080 on your computer you should see the Wordpress install screen:

Go through the process of creating the site, add a couple test posts, and then come back to this article.

Vagrant ssh

Now that we have Wordpress up and running it's time to make a copy of our database so we can easily restore it the next time we need to created this virtual box. In order to do that we need shell access to the server. Type the following into the command line:

vagrant ssh

This will automatically log you into the VM and you should see something like the following:

Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686)

 * Documentation:  https://help.ubuntu.com/
Welcome to your Vagrant-built virtual machine.
Last login: Fri Sep 14 06:22:31 2012 from 10.0.2.2
vagrant@precise32:~$ 

Now that you have direct access to the VM. Feel free to play around all you want. Remember that anything located in the /vagrant directory is mirrored to your working directory so you can damage it but everything else can easily be restored.

When you're done run the following commands mkdir /vagrant/data mysqldump -uroot -prootpass wordpress > /vagrant/data/initial.sql

This will make a backup copy of our Wordpress database so we can easily restore it the next time we recreate the VM.

Just to make sure everything is working we can look at our working directory and see that the sql file has been stored.

In order to leave the SSH session just type in exit.

Vagrant Halt

The VM isn't just idle. It's actually using some memory and CPU cycles just sitting there. So we don't tax our system too much we can shutdown VMs when we aren't using them. This is done using the vagrant halt command.

If we open VirtualBox before issuing this command we'll see that the VM is running:

When we run the command we'll see the following in the command line:

vagrant halt
[default] Attempting graceful shutdown of VM...

Then if we look back at the VirtualBox window we'll see this:

When we need to use this VM again we can just run vagrant up and it will be booted.

Vagrant Destroy

If you're like me you have a lot of projects going all the time but you don't always have enough room on your laptop hard drive for them. So from time to time you'll need to clean out the older images. In order to do this we'll use the vagrant destroy command. When we issue this command the following should be displayed:

vagrant destroy
Are you sure you want to destroy the 'default' VM? [y/N] y
[default] Destroying VM and associated drives...

After this has run we can check in VirtualBox to see that the VM is no longer there:

I would actually recommend trying this occasionally even if you don't need to because it provides you with an "opportunity" to test your backups.

Vagrant Up Again

A couple months go by and our client want something changed. Thankfully we're using vagrant so we can quickly get up and running again. All we have to do is run vagrant up again and the site will be restored to exactly the state it was in when we created the backups of the data. If you don't trust me try it.

Closing

As you can clearly see Vagrant provides several features that I think will make it a mainstay in everyones toolkit for years to come. It may be lacking in a nice user interface but it keeps me from having to repoint my root document as I switch projects or building 800 VMs. I've started using it for all my current projects and I'll be using it in any of my future projects as well.