Setting up WordPress + Nginx + HHVM For The Fastest Possible Load Times

One of the most enjoyable parts about writing code is writing efficient and performant functionality. No matter how fast your code is, if your server itself isn’t running to it’s maximum performance, you’re not delivering your site as fast as you could be. By setting up Nginx and the new HHVM, you can get your server delivering fully loaded pages faster than most servers answer a request.

HHVM is an open-source virtual machine designed for executing programs written in Hackand PHP. HHVM uses a just-in-time (JIT) compilation approach to achieve superior performance while maintaining the development flexibility that PHP provides.

It is worth noting that HHVM isn’t 100% compatible with all of PHP, so if you’re using obscure functions, you may run into an issue. In all of my testing of WordPress, I never once had this problem, but it is something to keep in mind.

In this post, we’ll set up both Nginx and HHVM from scratch, and then install and setup WordPress. If you follow this guide, you can have an insanely fast server setup in less than an hour.

This post is written for specifically Ubuntu 12.04 LTS and 14.04 LTS. The steps outlined will probably work on other versions, and with some modifications most likely anywhere. I prefer Ubuntu, though. I’ll be going through this on a Ubuntu 14.04 LTS installation.

I’m taking these steps on a brand new Digital Ocean VPS droplet, but you can use the server of your choice.

We want to start with a brand new installation, and install everything we will need. Our first step is updating all of the built in software, and then installing some small useful tools we will want.

$ sudo apt-get update
$ sudo apt-get install -y unzip vim git-core curl wget build-essential

The ‘-y’ flag we’re using for ‘apt-get install’ will tell apt that we want to answer ‘Yes’ to every question about whether or not we want to install a package. Because we’re also running as root (sudo), we want to only install from trusted sources, like the ones that are already added to Ubuntu.

We now want to install Nginx. It’s also possible to run HHVM with Apache, but our goal is speed and performance so we want to use Nginx. Nginx is a reverse proxy server, as well as a normal web server, and also gives us nice load balancing and caching.

The entire goal of Nginx and HHVM is to speed up everything our webserver does to create our final HTML pages that WordPress generates.

Installing Nginx is actually super simple on a new Ubuntu installation. We tell ‘apt’, our handy package manager how to find Nginx, and then we can just do a ‘apt-get install’ like we did previously.

$ sudo add-apt-repository -y ppa:nginx/mainline

1

After we do this, we want to do another at-get update to fetch the Nginx package we just told ‘apt’ about. Now we’re ready to install Nginx.

$ sudo apt-get install -y nginx

That’s it! Nginx is installed. We’ll configure it in just a bit.

Now we’re ready to install HHVM. For Ubuntu 12.04 and 14.04, we can follow the official guide.

Ubuntu 12.04:
$ sudo add-apt-repository -y ppa:mapnik/boost
$ wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add -
$ echo deb http://dl.hhvm.com/ubuntu precise main | sudo tee /etc/apt/sources.list.d/hhvm.list
$ sudo apt-get update
$ sudo apt-get install -y hhvm


Ubuntu 14.04:
$ 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 -y hhvm

So far, everything goes pretty smoothly. The longest part is probably waiting for the server to pull in all of the packages for the ‘apt-get update’ every time.

After installing HHVM, we get a pretty handy message.

********************************************************************
* HHVM is installed. Here are some more things you might want to do:
* 
* Configure your webserver to use HHVM:
* $ sudo /usr/share/hhvm/install_fastcgi.sh
* $ sudo /etc/init.d/nginx restart
* $ sudo /etc/init.d/apache restart
* $ sudo /etc/init.d/hhvm restart
* 
* Start HHVM at boot:
* $ sudo update-rc.d hhvm defaults
* 
* Run command line scripts with HHVM:
* $ hhvm whatever.php
* 
* Use HHVM for /usr/bin/php even if you have php-cli installed:
* $ sudo /usr/bin/update-alternatives --install /usr/bin/php php /usr/bin/hhvm 60
********************************************************************

This is nice because it tells us how to do our next few steps.

First we’ll run the first of the commands they tell us about to resart Nginx and install FastCGI using HHVM.

$ sudo /usr/share/hhvm/install_fastcgi.sh

After this, we want to tell Ubuntu to run HHVM on system start.

$ sudo update-rc.d hhvm defaults

And then we’ll restart HHVM, just for good measure.

$ sudo service hhvm restart  

At this point, HHVM is currently running, and will run whenever our server is started up. We can verify that HHVM is installed by creating a PHP file and making sure that HHVM can run it.

$ touch test.php
$ echo " test.php
$ hhvm test.php

We should get a return of “HipHop”. If that’s what you see, we know that HHVM is working perfectly.

At this point, we can start setting up the configuration for a website, but before we do that, there’s another handy command that our HHVM installation results screen gave us to have HHVM “pretend” to be PHP over the command line.

$ sudo /usr/bin/update-alternatives --install /usr/bin/php php /usr/bin/hhvm 60

What this lets us do is use PHP command line tools like WPCLI, PHPUnit, Composer, and all of those things, just like if we were running PHP instead of HHVM.

Let’s test this real quick by doing:

$ php -v

You should get a result like this:

3

One really nice thing about HHVM and Nginx is there is very little configuration we need to do to start working. We don’t really have to do anything at all and it is already running. The default file location of our files is “/usr/share/nginx/html”, and there is already a simple HTML file there. I like to make an alias so that I can quickly get to this location.

$ echo  "www=\"cd /usr/share/nginx/html\"" >> ~/.bashrc

This creates an alias so we can simply type “www” and change to our file directory.

At this point we want to install MySQL, so that we can actually use WordPress, and not just PHP files. Oddly, this portion has more configuration then anything we’ve done up to this point. After running the following command, we’ll get a password screen to set our root password.

$ sudo apt-get install mysql-server

4

Now we’re ready to install WordPress and set up Nginx to work with it.

We’re going to head to our Nginx HTML folder and grab a copy of wordpress and unzip it.

$ cd /usr/share/nginx/html
$ wget http://wordpress.org/latest.zip
$ unzip latest.zip

And we’ll also do a little housekeeping while we’re here.

$ mv wordpress/* .
$ rmdir wordpress
$ rm latest.zip
$ rm index.html

Now we’ll set up our MySQL database.

$ mysql -u root -pYOURPASSWORDHERE

5

Now we’re inside a MySQL prompt inside our SSH prompt inside our terminal program. Prompt-ception!

First we create a database, and then add a user, set that user’s password, give that user permissions, refresh MySQL privileges, and then exit out of our MySQL prompt. Make sure you write down this information, as you’ll need it for wp-config.php later.

$ create database databasename;
$ create user username@localhost;
$ set password for username@localhost = password("yourpasswordhere");
$ grant all privileges on databasename.* to username@localhost identified by 'yourpasswordhere';
$ flush privileges;
$ exit;

And now let’s create and edit wp-config.php. To edit the file, you can either SFTP in and edit this file locally, or use Nano or Vim on the server. We’ll use Vim.

$ cp wp-config-sample.php wp-config.php
$ vi wp-config.php 

For Vi, to start typing you want to hit ‘i’ to go into insert mode, and when you’re ready to save and quit, just hit ‘ESC’ and then ‘:wq’. It’s confusing, I know.

At this point, we’re ready to set up Nginx to be able to run WordPress. We’re going to edit /etc/nginx/sites-available/default.

$ sudo vi /etc/nginx/sites-available/default

We want to change two things in our configuration.

We want to change Nginx to also look for PHP files.

server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        root /usr/share/nginx/html;
        *index.php* index index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;
        include hhvm.conf;

And we want to change how files are loaded.

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ /index.php?q=$uri&$args;
                # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
        }

Your complete file should look like this:

# You may add here your
# server {
#	...
# }
# statements for each of your virtual hosts to this file

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
#
# Generally, you will want to move this file somewhere, and start with a clean
# file but keep this around for reference. Or just disable in sites-enabled.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

server {
	listen 80 default_server;
	listen [::]:80 default_server ipv6only=on;

	root /usr/share/nginx/html;
	index.php index.html index.htm;

	# Make site accessible from http://localhost/
	server_name localhost;
	include hhvm.conf;

	location / {
		# First attempt to serve request as file, then
		# as directory, then fall back to displaying a 404.
		try_files $uri $uri/ /index.php?q=$uri&$args;
		# Uncomment to enable naxsi on this location
		# include /etc/nginx/naxsi.rules
	}

	# Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests
	#location /RequestDenied {
	#	proxy_pass http://127.0.0.1:8080;    
	#}

	#error_page 404 /404.html;

	# redirect server error pages to the static page /50x.html
	#
	#error_page 500 502 503 504 /50x.html;
	#location = /50x.html {
	#	root /usr/share/nginx/html;
	#}

	# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
	#
	#location ~ \.php$ {
	#	fastcgi_split_path_info ^(.+\.php)(/.+)$;
	#	# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
	#
	#	# With php5-cgi alone:
	#	fastcgi_pass 127.0.0.1:9000;
	#	# With php5-fpm:
	#	fastcgi_pass unix:/var/run/php5-fpm.sock;
	#	fastcgi_index index.php;
	#	include fastcgi_params;
	#}

	# deny access to .htaccess files, if Apache's document root
	# concurs with nginx's one
	#
	#location ~ /\.ht {
	#	deny all;
	#}
}


# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
#	listen 8000;
#	listen somename:8080;
#	server_name somename alias another.alias;
#	root html;
#	index index.html index.htm;
#
#	location / {
#		try_files $uri $uri/ =404;
#	}
#}


# HTTPS server
#
#server {
#	listen 443;
#	server_name localhost;
#
#	root html;
#	index index.html index.htm;
#
#	ssl on;
#	ssl_certificate cert.pem;
#	ssl_certificate_key cert.key;
#
#	ssl_session_timeout 5m;
#
#	ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
#	ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
#	ssl_prefer_server_ciphers on;
#
#	location / {
#		try_files $uri $uri/ =404;
#	}
#}

Now we need to restart Nginx, and start the WordPress installation.

$ sudo service nginx restart

At this point you can navigate in your browser to your site and you should be good to go!

 

load-times

Here’s a screenshot a stresstest of a site running default 2014, with the steps outlined above. As you can see, these results are amazing for a $5/month server.

This entry was posted in Community, Employee Post, General, How To, Snippets, Team, Tutorial, WordPress, WP Updates. Bookmark the permalink.

16 Responses to Setting up WordPress + Nginx + HHVM For The Fastest Possible Load Times

  1. Hi,
    Good article :)
    Did you do any benchmarks (Apache Bench for exemple) between a classic stack (Nginx, PHP-FPM) vs HHVM ?

  2. Nice post – glad to see more people using HHVM. A while ago I setup a script to do this and we use it to spin up boxes on aws or digitalocean. https://github.com/chuckreynolds/hhnginx

    Looks like our setups are on par with each other… if anything I’m totally open to pull requests on that to make it better – that’s why I left the repo public.

    Cheers!

  3. Robin says:

    I’m confused about something in the mySQL section. Shouldn’t this

    $ mysql -u root -pYOURPASSWORDHERE

    be

    $ mysql -u root -p YOURPASSWORDHERE <— note the space?

    and when setting up the database, in this part, should the "databasename" be a string that the user has to change just like YOURPASSWORDHERE?

    $ create database databasename;

    • Brad Parbs says:

      Actually, it is:
      $ mysql -u root -pYOURPASSWORDHERE

      MySQL is a bit weird, because there isn’t a space for the -p flag.

      and yes, you are correct – you should choose a unique database name.

      • Robin says:

        Thanks Brad, sorry for the dumb questions! I’m new to all this.

        I’m assuming that “databasename” isn’t the only thing which should be changed or customized here:

        $ create database databasename;
        $ create user username@localhost;
        $ set password for username@localhost = password(“yourpasswordhere”);
        $ grant all privileges on databasename.* to username@localhost identified by ‘yourpasswordhere';
        $ flush privileges;
        $ exit;

        My guess is username@localhost should also be changed. Is this the same as root@DOdropletname?
        (I’m using digitalocean droplets), or something else?

        thanks

        • Dee says:

          No it’s not root. You don’t use the mysql root user to access your website database. You use an unpriviliged user. That is why he gives the user priviliges over the wordpress database and only the WP database. Root wouldn’t need that because he has free run over all databases which is also why you don’t use it cause then one compromized site could wreck all the databases on the server.

  4. Robin says:

    I’m stuck at this last part: We want to change two things in our configuration. We want to change Nginx to also look for PHP files.

    I have no idea what I’m supposed to do? The two code snippets shown below “We want to change Nginx to also look for PHP files.” and “And we want to change how files are loaded.” are already there. Am I supposed to click “i” again like the above part of the tutorial? why? then exist with :wq? how do I copy/paste the /etc/nginx/sites-available/default file that you’ve provided and be done with it? Sorry if this is a daft question, I feel rather daft right now :(

    • Kieran says:

      Vim and vi are text editors you can use in the command line. Since you don’t have a mouse, it’s useful when working in a terminal over SSH. It is a bit complicated to use without a few tutorials. Type
      “vimtutor” in the terminal if you’re interested in learning it.
      If you just want to edit the files and be done with it, using sFTP would probably be your best bet. Download the files locally, edit them, then upload.

  5. Robin says:

    when trying to install nginx using “sudo add-apt-repository -y ppa:nginx/mainline” I get this error:

    Cannot add PPA: ‘ppa:nginx/mainline’.
    Please check that the PPA name or format is correct.

    I googled and found these two lines:

    sudo apt-get install –reinstall ca-certificates
    sudo -E add-apt-repository ppa:linrunner/tlp

    neither worked. I still get the same error

  6. Pingback: Problematic Ngnix config - HelpDesk

  7. Steven Jones says:

    Hi,

    I copied the gist that is linked to for the /etc/nginx/sites-available/default file., but then when I go to restart nginx then I get it fails.

    At the moment by site is showing a 403 forbidden error. I’m quite sure that the other steps have been completed successfully.

    Any ideas what it might be?

    Cheers.

  8. Dee says:

    What’s in the hhvm.conf file that you are including in nginx? What about file system permissions? Does hhvm run as a normal user or as nginx or what?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>