Creating a development Virtual Machine – Part one: Ubuntu, Apache, MySQL, PHP

If you do web development on Windows, you’re probably used to making do. XAMPP can go a long way, as can Cygwin, but once you want to start playing around with anything state-of-the-art, you’ll start feeling left behind. The solution? Set up a virtual machine running Linux, so you can take advantage of the latest and greatest in their native environments.

Set up Ubuntu

For this purpose, we’re going to be using VirtualBox – originally by Sun, recently(ish) purchased by Oracle. For my money, it still has the best mix of ease-of-use, features, and price (free). You can download the latest version here.

And while you’re at it, go download the latest version of Ubuntu Server. For this article, we’re using 11.10 (Oneiric Ocelot), which you can download here. Grab the 64-bit version.

Install VirtualBox. Before you run it the first time, you’ll want to right-click the icon and choose “Properties”. Then go to the “Compatability” tab and click the checkbox next to “Run this program as an administrator”. If you want any symlinks you create in Ubuntu to be recognized by Windows, you MUST be running the program as an administrator. Windows is funny like that.

Launch VirtualBox, then create a new virtual machine and set the Ubuntu ISO as the CD/DVD drive. (Under the Storage menu in Settings, click on the CD-looking thing under the IDE controller). Also, make sure to set up Networking as “Bridged”. Start up the virtual machine, then follow Ubuntu’s really slick install process. The only application you’ll want to install during setup is the OpenSSH server. We’ll install the rest ourselves.

When all is said and done, restart it and log in. Now the fun begins!

Set up your networking

Let’s set up a static IP address so we don’t have to keep figuring out what dynamic IP the machine obtained.

$ sudo nano /etc/network/interfaces

Edit the last part to look like this:

# The primary network interface
auto eth0
iface eth0 inet static

You’ll have to fiddle with those numbers a bit depending on your networking configuration. Ensure primarily that the address you supply isn’t taken by anyone else on your network, and that the gateway matches the one you get when you type “ipconfig” in Windows. Restart your network to get the changes recognized.

$ sudo /etc/init.d/networking restart

Once that’s working, start up Notepad as Administrator in Windows (right-click and “Run as administrator”). Then open up “C:\Windows\System32\drivers\etc\hosts” (no extension) and add your new machine’s IP address. Now you can do things like SSH directly to the hostname or type the hostname in your browser instead of remembering the IP address.

Install Apache

This is easy, and can be handled entirely through automated utilities built-in to Ubuntu.

$ sudo apt-get install --reinstall language-pack-en
$ sudo dpkg-reconfigure locales
$ sudo apt-get install gcc make wget cron curl
$ sudo apt-get install apache2 apache2-mpm-prefork apache2-prefork-dev apache2-utils apache2.2-common

One quick thing, to avoid getting the error “Could not reliably determine the server’s fully qualified domain name, using for ServerName” every time we restart, we need to make a small change.

$ sudo nano /etc/apache2/httpd.conf

Add the following line and save.

ServerName localhost

Install MySQL

Nearly as easy. Start with the automated utilities.

$ sudo apt-get install mysql-server-5.1

No need to give MySQL a root password on your development box. Then edit the MySQL config file.

$ sudo nano /etc/mysql/my.cnf

Change the “bind-address” line in that file to point the the local server IP. ( in the example above.)

Then, it’s worthwhile allowing root to log in from machines other than localhost. That way you can use whatever Windows GUI tool you like to log in to your virtual machine’s MySQL installation.

$ mysql -u root
GRANT ALL ON *.* TO 'root'@'%';
$ sudo service mysql restart

Install PHP

Now we could just use the built-in version of PHP, if we felt like taking the easy road. But that’s no fun. Let’s install PHP 5.4 RC2, so we can play around with all the new features. As an added bonus, if you get comfortable with compiling PHP now, you can recompile with whatever version and options you want later for when there’s a new release of PHP, and you don’t have to wait for your package maintainers to update.

First we need a bunch of build tools.

$ sudo apt-get build-dep php5
$ sudo apt-get install libxml2 libxml2-dev libzip-dev libbz2-dev curl libcurl4-openssl-dev libcurl3 libcurl3-gnutls libjpeg62 libjpeg62-dev libpng12-0 libpng12-dev libmcrypt-dev libmcrypt4 libxslt1-dev libxml2-dev

Next, grab and extract PHP 5.4.

$ cd ~/
$ mkdir tmp
$ cd tmp
$ wget -O php-5.4.0RC2.tar.gz
$ tar xvfz php-5.4.0RC2.tar.gz

Now, we configure and compile. This takes a while. Feel free to grab a sandwich or something.

$ cd php-5.4.0RC2
$ ./configure --with-apxs2=/usr/bin/apxs2 --with-config-file-path=/etc/php5 --with-mysql=mysqlnd --enable-inline-optimization --disable-debug --enable-bcmath --enable-calendar --enable-ctype --enable-force-cgi-redirect --enable-ftp --with-gd --enable-memory-limit --disable-sigchild --enable-trans-sid --with-ftp --with-jpeg-dir=/usr --with-png-dir=/usr --with-zlib=yes --with-zlib-dir=/usr --with-openssl --with-xsl=/usr --with-gd --with-mcrypt=/usr --with-mhash=/usr --enable-mbstring=all --with-curl=/usr/bin --with-curlwrappers --enable-mbregex --enable-zend-multibyte --with-bz2=/usr --with-mime-magic --with-iconv --with-pdo-mysql=mysqlnd --enable-fileinfo --with-pear --enable-exif
$ make
$ sudo make -i install

Of course, now we have to ensure that Apache knows about PHP.

$ sudo ln -s /usr/local/bin/php /usr/bin/php
$ sudo nano /etc/apache2/mods-available/php5.conf

Add the following lines and save.

AddType application/x-httpd-php .php .phtml .php3
AddType application/x-httpd-php-source .phps

Now enable PHP (and mod-rewrite, while we’re at it).

$ sudo a2enmod php5
$ sudo a2enmod rewrite

It wouldn’t hurt to ensure that PHP is configured how we like it, either.

$ cd /etc
$ sudo mkdir php5
$ cd php5
$ sudo cp ~/tmp/php-5.4.0RC2/php.ini-production php.ini
$ sudo nano php.ini

Ensure the following are set:

date.timezone = America/Toronto (Or whatever is closest to you.)
short_open_tag = On
error_reporting = E_ALL
display_errors = On
log_errors = On
error_log = /var/log/php.log
max_execution_time = 30
memory_limit = 128M
mysql.default_socket = /var/run/mysqld/mysqld.sock

Now make sure that the logfile we specified exists and is writable.

$ sudo touch /var/log/php.log
$ sudo chmod a+rw /var/log/php.log

Finally, restart Apache!

$ sudo service apache2 restart

You should be able to visit the address of your new virtual machine (or the alias you set up in hosts) now and get your basic Apache display.

Extra step – VirtualHosts

I presume you’re going to want to develop multiple projects without having to create separate virtual machines for them all. That’s easy! We’ll just set up an Apache VirtualHost for each one.

$ sudo nano /etc/apache2/sites-available/{development url}

Add the following, then save.

<VirtualHost *:80>
    ServerName {development url}
    DocumentRoot /home/{user}{/{development url}/

    <Directory /home/{user}/{development url}/>
        AllowOverride All

Now enable your site and reload Apache.

$ sudo a2ensite {development url}
$ sudo service apache2 reload

Extra step – Shared folders

I bet you’re wondering how you’re supposed to get files on and off that machine, huh? Well, since you installed OpenSSH, you have options there, but there’s an easier way. You can just share that folder you were referencing above with a specific folder on your host machine. Make changes in Windows, and it will (usually) be reflected in the virtual machine. I’ve noticed a few times that when I’ve created a file, the Ubuntu host flips out and doesn’t properly recognize it, but a quick restart fixes it, and machine restarts are usually like ten seconds, max.

First, make sure that folder above exists on your virtual machine, then click on the “Devices” menu in VirtualBox, then click “Install Guest Additions”. This makes a virtual CD available to your virtual machine that contains the actual guest additions we’re going to install.

$ sudo mount /dev/cdrom /media/cdrom
$ cd /media/cdrom
$ sudo ./

Ignore the crap about failing – we don’t have any GUI installed on the server, so whatever. Now shut down the machine (“sudo shutdown -h now”), and open up the Settings menu and click on Shared Folders. Click the little Folder/Plus icon on the right, and choose a Folder Path from your Windows box, and remember the Folder Name it gives you (or make up your own). Hit “OK” and start that machine back up.

We’re going to set up this machine to automatically mount that folder every time it boots up.

$ sudo nano /etc/init.d/rc.local

Add the following just after “do_start() {“:

    mount -t vboxsf {Folder Name} /home/{user}/{development url}

Now restart to check it out.

$ sudo restart -r now
# After restarting...
$ cd /home/{user}/{development url}
$ ls

You should see all the files from your Windows machine. If you don’t, well… ugh. Okay, I got this fixed by re-running the script, the re-mounting. No clue why that worked, but it worked, so… yeah. Not questioning it.

What Else Could We Possibly Do?

Hot damn, that’s a lot of stuff, isn’t it? Well, we still have Memcache, Memcached (yes, both), and Redis to install. These are all optional, but super-neat tools, and practically essential if you want to write anything that scales well these days.

For now though, you should have a good base to work from. If anything doesn’t work, let me know, but I followed along with my own VM while I was writing this, so I’m pretty confident I didn’t skip anything.

Code faster with simple Sublime Text improvements
Rage-quit support for fish shell
Gulp.js – an AMAZING build system!
  • T. T. Smyouf

    T. T. SmyoufT. T. Smyouf


    Very good article.

    I will set up my development virtual machine today!

  • GT Gaastra

    GT GaastraGT Gaastra


    This article was really helpful, using this style of a development server really helps in my workflow :-).

    I’ve got a small addition to the Shared Folder-section. The problem with files not updating properly seems due to apache2 behavior to not read static files itself but letting the OS do this. Somehow this breaks with Virtualbox. ( ). I mostly ran into this when I updated an image file and had to restart the VM to see the difference.

    Changing this setting is luckily easy:
    – Open the virtualhost file in /etc/apache2/sites-available/.
    – Add “EnableSendfile Off” before one of the sections.
    – restart apache.
    – Enjoy :-)
    – Don’t forget to hit ctrl+f5 to force re-download the images in your browser ;-).

  • Dan Hulton

    Dan HultonDan Hulton


    Good catch!

    The strange thing is, I just switched back to this style of development recently, and a co-worker of mine encountered and found the fix to that same problem yesterday!