Otaqui.com Blog

Setting up a Virtualbox Virtual Machine for Web Development with Multiple Sites using mod_vhost_alias and VirtualDocumentRoot

Hopefully as a developer you are using a virtual machine for local development. This makes sense unless your target platform is very much the same as your working one – not the case for a lot of people developing LAMP / Ruby / Python / Node. However, this can still present problems when you want to have multiple websites running and don’t want the sacrifice of putting everything in a subdirectory. I’m going to explain my setup which uses subdomains for a virtual machine, and apache’s vhost_alias module with the VirtualDocumentRoot directive.

You will be able to create a directory called “super/” to hold your project, and immediately browse to it using the subdomain “http://super.virtualbox.me” without any further configuration – neat!

Ready made stacks for Window and the Mac, like XAMPP and WAMP, are alright but you still aren’t using the platform that you will likely be deploying to, to whit: Linux.

I run a single VirtualBox VM for most LAMP development, and use this machine to serve the various sites I want to work on. All the actual files reside on my host computer, and are shared to the VM. My VM runs Ubuntu and the host is OS X, so these instructions are guided toward that.

Step 1, Create a VM and install server software

First off – create a new Ubuntu VM and install the OS, to your taste. I actually use the desktop version of Ubuntu, since there are some GUI programs I like to use and this is fine for me. For what it’s worth, I also assign Virtualbox and VMs to a second OS X “space”, so they don’t get in the way of my main workflow which is all on the host machine.

With Ubuntu adding the software we need is easy:

$ sudo apt-get install apache2 mysql-client mysql-server php5 php5-mysql

Step 2: Setup the network

So far so good. By default though, Virtualbox VMs can’t communicate directly with the host machine – they get NAT which allows them to out to the internet instead. You will probably want to create one host-only network for general VMs, and a dedicated one which is only used by this newly created development machine, since you can then control the IP addresses without messing with the defaults of others.

Open up Virtualbox and get into the preferences (make sure this is the Virtualbox preferences, not the individual machine preferences). In the “Network” panel, create 2 “host only” networks, by default these will be called vboxnet0 and vboxnet1. Leave the defaults of vboxnet0 as they are, we’re going to use vboxnet1 for the development machine.

vhost_alias_1_vbox_network

In the Adapter pane of the vboxnet1 editor, choose the following settings:

vhost_alias_2_vboxnet1_adapter

And in the DHCP pane, these:

vhost_alias_3_vboxnet1_dhcp

Since we’re only going to have one machine on this network, we can use DHCP instead of setting a fixed IP, and still rely on the fact that it will always be 192.168.201.101. There’s a bonus coming up later in this regard :)

Make sure the VM is turned off, and then open up it’s settings. In the network panel there, you want to make sure that it has two network adapters – one for the host-only network we just created and a secondary one for NAT so that the VM has access out to the internet.

For “Adapter 1” choose the following:

vhost_alias_4_vmnetwork_adapter_1

For “Adapter 2” choose the following:

vhost_alias_5_vmnetwork_adapter_2

Note – I had trouble with some of this if the host-only adapter wasn’t the first, i.e. you want Adapter 1 to be attached to the host only network and Adapter 2 to be attached to NAT.

Step 3: Access the Host from the Guest

The default Shared Folders that you get with Virtualbox Guest Additions kind of suck. I use SSHFS to share files since I’ve actually found this much easier to manage the Samba / CIFS. To to this you will need to enable “Remote Login” in the OS X System Preferences / Sharing panel.

Check that you’ve done this correctly – get into a terminal in the ubuntu guest virtual machine, and try SSHing into the (in my case Mac) host. Note that, because of how we setup the host-only network, the IP of the host machine from the guest’s point of view is 192.168.201.1.

$ ssh USERNAME@192.168.201.1

Hopefully that worked, and now you need to setup SSH key-based logins instead of requiring a password. In the Guest:

user@guest $ ssh USERNAME@192.168.201.1

Then you’ll need to install SSHFS in the Ubuntu virtual machine (accept the defaults for the first command and do not enter a passphrase). Run this on the GUEST:

user@guest $ ssh-keygen -t rsa
user@guest $ cat ~/.ssh/id_rsa.pub

Copy the output of the second command (the public part of the key) and add it to the authorized_keys file. Find, or quite likely create, the ~/.ssh/authorized_keys on the host (I’m making sure the permissions are correct here, and using the mac pbpaste command … use any text editor you prefer). On the HOST:

user@host $ touch ~/.ssh/authorized_keys
user@host $ chmod 600 ~/.ssh/authorized_keys
user@host $ pbpaste >> ~/.ssh/authorized_keys

Try logging into the host from the guest to the host again to check that you no longer need a password:

user@guest $ ssh USERNAME@192.168.201.1

Step 4: Mount an SSHFS share

On my host machine I have use the folder /Users/pete/VirtualWWW to keep all the projects I want to serve, so make that directory on the host (obviously use your own username!)

Then add this to the /etc/fstab file on the guest (it’s all one line):

sshfs#pete@192.168.201.1:/Users/pete/VirtualWWW /var/www fuse comment=sshfs,defaults,idmap=user,allow_other,uid=33,gid=33,transform_symlinks,noauto,users,exec,BatchMode=yes,nonempty 0 0

Note the following features: mounts at /var/www, makes the mount owned by the www-data user and group (uid and gid 33), doesn’t auto mount at boot.

Having done this, you should now be able to mount the host folder in the guest! Make sure the ~/VirtualWWW folder has something in it, and then run this on the guest:

user@guest $ sudo mount /var/www
user@guest $ ls -l /var/www

Hopefully you’ll see whatever you added to VirtualWWW and that it’s owned by www-data. Now we need to automatically mount this when we boot, but after the network has come up. I found a script called “mountsshfs” and I use a slightly modified version which you can find here: https://gist.github.com/pete-otaqui/6822228 .

Copy the contents of my mountsshfs to /etc/network/if-up.d/mountsshfs in the guest. Also get the unmountsshfs from the original thread, and copy that to /etc/network/if-down.d/umountsshfs. Make sure both files are executable and owned by root.

Reboot the guest and you should now find that the share is automatically mounted at /var/www.

Step 5: Configure Apache with mod_vhost_alias and VirtualDocumentRoot

OK, we can now write code on the host and see it in the guest. Now let’s configure Apache so that every directory in our share equates to a subdomain, which we do by using the vhost_alias module and the VirtualDocumentRoot directive.

Create the file /etc/apache2/sites-available/virtualbox and add the following content:

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        UseCanonicalName Off
        VirtualDocumentRoot    /var/www/%1/public
        LogLevel debug
        <Directory /var/www>
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>
</VirtualHost>

Note that I’ve specified that each site’s public directory is actually a folder called “/public” inside the specific subdirectory. More and more frameworks expect you to separate library code from publicly available code, so this is a good idea. The %1 used in the VirtualDocumentRoot directive means the “the first subdomain part” – i.e. if you went to http://super.virtualbox.me/index.php, this would load the file /var/www/super/public/index.php.

Now disable the default site, enable the module and enable the new “virtualbox” site:

user@guest $ sudo a2dissite default
user@guest $ sudo a2enmod vhost_alias
user@guest $ sudo a2ensite virtualbox
user@guest $ sudo service apache2 restart

Step 6: Try it out (with a surprise DNS bonus)

Go for it. Assuming you’ve created /Users/USERNAME/VirtualWWW/super/public/index.php go ahead and visit http://super.virtualbox.me/index.php .

If that worked you might be wondering … “Hold on, what about DNS? How did the domain resolve to my local machine?” Well, there’s the surprise. I bought the “virtualbox.me” domain specifically for this purpose: it and all subdomains will resolve to 192.168.201.101. This is a neat trick I picked up from the BBC’s Forge platform, where developers are required to setup a virtual machine with a specific IP address and the domain sandbox.dev.bbc.co.uk always resolves to it. I’ve expanded on that a bit, so that I can map subdomains to project folders without any further work.