How To: Set Up and Configure HHVM on Ubuntu 14.04

HHVM or HipHop Virtual Machine is an virtual machine that executes code written in PHP or Hacklang using a just-in-time (JIT) compilation approach to realize greater performance than would otherwise be achievable.

In this post, I will be running through how to install and set up HHVM on an Ubuntu 14.04 Server set up using an Apache Virtual Hosts configuration. In the end, you will be able to run traditional and HHVM-based PHP-powered web applications side-by-side on the same system.

Install HHVM

We don't need to all get fancy compiling anything, so we will simply install the prebuilt HHVM packages for Ubuntu 14.04 (64-bit). Run the following...

wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add -  
echo deb http://dl.hhvm.com/ubuntu trusty main | sudo tee /etc/apt/sources.list.d/hhvm.list  
sudo apt-get update  
sudo apt-get install hhvm  

Source: https://github.com/facebook/hhvm/wiki/Prebuilt-Packages-on-Ubuntu-14.04

Next, we need a way to hook HHVM into our Apache web server. This is done using FastCGI, which we will need to install. Luckily, HHVM provides a shell script to set this up. Run the following...

sudo /usr/share/hhvm/install_fastcgi.sh  

Also, to ensure HHVM fires up at boot, run this...

sudo update-rc.d hhvm defaults  

Configure HHVM and Apache Virtual Hosts

The install script will ask you to restart both HHVM and Apache. Don't just yet, otherwise your site visitors will be seeing some 404 action coming their way. Instead, open hhvmproxyfcgi.conf and comment the single ProxyPassMatch line that's in there.

sudo nano /etc/apache2/mods-available/hhvm_proxy_fcgi.conf  
# ProxyPassMatch ^/(.+\.(hh|php)(/.*)?)$ fcgi://127.0.0.1:9000/var/www/$1

This stops all PHP/Hack scripts from being routed through FastCGI, this will allow you to choose which web applications you'd like to send through HHVM. If you're only running a single web application in your web server root, you don't need to do this, but I feel it's a smart move regardless.

Right now HHVM is running but has no way for scripts to be handed off to it. We need to add the ProxyPassMatch configuration to the Virtual Host Configuration for the web applications we'd like HHVM to power. Add this to each Virtual Host instance in each configuration file (both secure and non-secure traffic covered in the example below.).

sudo nano /etc/apache2/sites-available/hhvm.example.com.conf  
# HHVM Example - hhvm.example.com

<VirtualHost *:80>

    ServerName hhvm.example.com

    DirectoryIndex index.php
    DocumentRoot /var/www/sites/hhvm.example

    ProxyPassMatch ^/(.+\.(hh|php)(/.*)?)$ fcgi://127.0.0.1:9000/var/www/sites/hhvm.example/$1

</VirtualHost>  
<VirtualHost *:443>

    ServerName hhvm.example.com

    DirectoryIndex index.php
    DocumentRoot /var/www/sites/hhvm.example

    SSLEngine On
    SSLCertificateFile   /etc/ssl/certs/hhvm.crt
    SSLCertificateKeyFile    /etc/ssl/private/hhvm.key
    SSLCACertificateFile  /etc/ssl/certs/hhvm.ca.crt

    ProxyPassMatch ^/(.+\.(hh|php)(/.*)?)$ fcgi://127.0.0.1:9000/var/www/sites/hhvm.example/$1

</VirtualHost>  

Pro-Tip

You can also designate another port to explicitly use HHVM if you'd like to quickly A/B test the performance gains. To do this, simply open the port in /etc/apache2/ports.conf

Listen 8080  

Then in your Virtual Host configuration, create another instance with the new port specified and add the ProxyPassMatch configuration to the one you'd like to run HHVM on.

<VirtualHost *:80>

    ... 

</VirtualHost>  
<VirtualHost *:8080>

    ... 

    ProxyPassMatch ^/(.+\.(hh|php)(/.*)?)$ fcgi://127.0.0.1:9000/var/www/$1

</VirtualHost>  

Restart Apache and HHVM

Now, we are ready to kick Apache and HHVM in the head. Restart both services...

sudo service apache2 restart  
sudo service hhvm restart  

The web application should load as you would normally expect, only a bit quicker.

I'm getting 503'd!

If you jumped over to your web application only to be greeted with a 503 error, this means you just need to fire up the daemon manually this first time or restart. Subsequent restarts will automatically fire this up.

hhvm --mode daemon -vServer.Type=fastcgi -vServer.Port=9000  

Testing

It's not immediately apparent whether your code is being passed through HHVM. There are a few ways to test this.

Client-Side

The easiest way to check if HHVM is doing it's thing is to inspect the response headers we get back from our web application. At the bottom of the header list, you will find X-Powered-By which should show HHVM/3.2.0.

To get to this, you can right click on the page, inspect any element, then click the "Network" tab in the developer tool panel. It will appear to be empty or nearly empty. Refresh the page and it will repopulate fully. Scroll to the top of the list and click the first item, it will be labeled with the domain name (hhvm.example.com in my case).

Server-Side

Drop the following into your application root and then access this script. If it returns PHP, it's not working. If it returns HHVM, you're golden!

<?php 

if (defined('HHVM_VERSION')) {

    echo "HHVM";

} else {

    echo "PHP";

}