High-performance WordPress installation using Nginx, MariaDB and HHVM from scratch in Ubuntu 13.10
Not so recently I released my studio website which uses WordPress, so I had to set up a web server from scratch, I found this a good oportunity to write something helping people easily set up a high performance WordPress using the latest mainstream technology, hopefully you find this article helpful :)
Step 1: Setting up #
So you just created a new droplet, a shining Ubuntu 13.10 installation, you ssh’d into
it and are presented with a lovely virgin terminal. First things first! Update and upgrade
all installed packages
$ apt-get update && apt-get upgrade
After a few moments the command will finish and we’re ready to install all our software.
The next thing to do is creating a user so we don’t use root
as it’s a very bad practice!
$ useradd deploy
We named our new user deploy
you should name it whatever you want, just remember to make
the appropiate changes. Once we have our new user let’s create a home directory for it!
$ mkdir /home/deploy
$ chown deploy:deploy /home/deploy -R
We created a new directory for our user and add it as owner so she can freely work inside
her home directory.
Finally let’s create a password for our deploy
user
$ passwd deploy
Use any password you want, it will be used for sudo commands as well as logging in though
ssh (if you follow the optional step we’ll use SSH keys to log in instead of the unsafe
passwords! but that step is optional).
Let’s configure the users who can use sudo
$ visudo
Comment all existing grant lines and add the following at the end of the file
root ALL=(ALL) ALL
deploy ALL=(ALL) ALL
Save and exit. Now only root
and deploy
can use sudo. Another change we should make
now is disallow root login though ssh, for this edit the /etc/ssh/sshd_config
file and
add the following at the end
PermitRootLogin no
Finally restart the ssh service so it loads the changes
$ service ssh restart
So far so good! Let’s log out root
and ssh as deploy
instead, from now on we’ll work
with that user.
Let’s secure our server a bit
$ sudo apt-get install fail2ban
Fail2ban is an app which bans a certain IP from logging into your server if it has failed
the password too many times, it comes with nice defaults so that will do.
Another thing we have to do is enable the firewall, this is easy
$ sudo ufw allow 22
$ sudo ufw allow 80
$ sudo ufw allow 443
$ sudo ufw enable
We only allow the ports for ssh (22) and nginx (80 and 443 if https).
Step 1.5: Public key authentication #
If you want to secure your server a bit more we can require all ssh sessions to
authenticate using a public key instead of a password, this is considerably safer.
First step is creating a ~/.ssh/authorized_keys
file and pasting your public key
(most likely id_rsa.pub) there.
$ mkdir ~/.ssh
$ vim ~/.ssh/authorized_keys
Once you have saved your public key there let’s change the permissions of that file
$ chmod 400 ~/.ssh/authorized_keys
Now try ssh-ing as deploy
, you should be able to log in without typing the root
password (your public key might require a password, that’s up to you).
The final step is to require ssh to authenticate only with public keys, edit /etc/ssh/sshd_config
once again and append this line:
PasswordAuthentication no
Finally restart ssh
$ sudo service ssh restart
That’s it!
Step 2: Setting up the webserver #
We’ll use Nginx as our webserver, thankfully setting it up is quite easy!
$ sudo apt-get install nginx
That was fast. Let’s move onto MariaDB, because Ubuntu doesn’t include a package for MariaDB we have to manually add a package repository so we can easily install it with apt-get
:
$ sudo apt-get install software-properties-common
$ sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db
$ sudo add-apt-repository 'deb http://download.nus.edu.sg/mirror/mariadb/repo/10.0/ubuntu saucy main'
Note: If you are not running Ubuntu 13.10 you can see what package repository to use in MariaDB official docs.
We can now install MariaDB as follows
$ sudo apt-get update
$ sudo apt-get install mariadb-server
It will prompt us for a password, choose a password for your MariaDB root user and
continue, the installation is pretty quick. Once it ends, let’s secure it running
$ mysql_secure_installation
It will prompt us quite a lot of questions, if you don’t want to change the root
password answer No
to the first one, the rest are Yes
.
Finally let’s install HHVM
, Facebook’s PHP interpreter. Because HHVM uses JIT
compilation code execution is quite fast, together with Nginx we’ll make WordPress fly!
Here we have the same issue as with MariaDB, let’s add the custom package repository:
$ wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add -
$ echo deb http://dl.hhvm.com/ubuntu saucy main | sudo tee /etc/apt/sources.list.d/hhvm.list
Now install it
$ sudo apt-get update
$ sudo apt-get install hhvm
That will install HHVM in our server, there is one final step, we have to configure HHVM to work with Nginx, luckly this is super easy!
$ sudo /usr/share/hhvm/install_fastcgi.sh
Let’s try it out
$ sudo echo "<?php phpinfo(); ?>" > /usr/share/nginx/html/phpinfo.php
If you navigate to http://yourip/phpinfo.php
you should see a little message saying
“HHVM”. Success!
Step 3: Installing WordPress #
Let’s install WordPress, to make it easy I’ll make a soft link to the nginx public
folder and set deploy
as owner
$ sudo ln -s /usr/share/nginx/html ~/www
$ sudo chown deploy:deploy /usr/share/nginx/html -R
We can now just work inside ~/www
$ cd ~/www
$ wget http://wordpress.org/latest.tar.gz
$ tar -xvzf wordpress.3.x.x.tar.gz
$ mv wordpress/* .
$ rm -rf wordpress
$ rm wordpress.3.x.x.tar.gz
The above code will download wordpress, extract it into wordpress/
and move the
contents to that folder to our www root, finally it removes the empty wordpress folder
as well as the .tar.gz file.
If you navigate to your site index you’ll be prompted to install WordPress, let’s first
create a database
$ mysql -u root -p
MariaDB will ask for your root password, once you enter it we can create a new database
$ CREATE DATABASE wordpress;
That’s it! We have our database, I named it wordpress
but you can choose any name you
want. Finally exit the SQL prompt
$ exit
Now proceed with wordpress installation, use wordpress
for the database, localhost
for host, root
for user and the password you chose when installing MariaDB.
Good! We now have a fully functional WordPress running on Nginx + MariaDB + HHVM!
Step 4: Installing sendmail #
To be able to send mails though our server we need to install a MTA, postfix beeing the
simplest option.
$ sudo apt-get install sendmail
$ sudo sendmailconfig
Just accept everything the configuration asks, it has pretty good defaults. To test
your sendmail installation you can run
$ echo "Hello!" | sendmail myemail@gmail.com
If it takes like a minute to send, you have to configure your IPv4 hostname
$ sudo vim /etc/hosts
And add the following line
127.0.0.1 localhost.localdomain localhost HOST_NAME
HOST_NAME
is the value the command hostname
returns.
If you have a domain you can replace the code above with
127.0.0.1 mail.mydomain.com mydomain.com HOST_NAME
Restart sendmail and try again
$ sudo service sendmail restart
$ echo "Hello!" | sendmail myemail@gmail.com
It should now work pretty quickly!
Step 5: Optimization #
Let’s start optimizing! First lets make Nginx cache our static assets as well as gzip
js and css files. To do this let’s update the global nginx configuration file
$ sudo vim /etc/nginx/nginx.conf
The gzip section must look like this
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
We’ll gzip css, json, js and xml files.
Let’s now add expiration time on our assets so browsers cache them, for this we’ll
update the per-site configuration file
$ sudo vim /etc/nginx/sites-available/default
Add the following definition inside server { }
location ~* \.(js|css|png|jpg|jpeg|gif|ico|woff|ttf|svg|otf)$ {
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
}
We now have proper headers to our static assets so browsers can cache them!
WordPress Cache #
Another thing we can do to improve performance is using W3 Total Cache. Download it, install it and enable it.
This plugin offers several options, we’ll just use Page Cache and set it to
Disk(Enhanced).
We now have to properly update our nginx site configuration
$ vim /etc/nginx/sites-available/default
And make the following changes
server {
# ...
set $cache_uri $request_uri;
# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
set $cache_uri 'null cache';
}
if ($query_string != "") {
set $cache_uri 'null cache';
}
# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail ).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations .php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
set $cache_uri 'null cache';
}
# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
set $cache_uri 'null cache';
}
# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
try_files /wp-content/cache/page_enhanced/${host}${cache_uri}_index.html $uri $uri/ /index.php?$args;
}
# ...
}
Pretty URLs #
One final thing to do is make WordPress permalinks play nice with Nginx, normally
WordPress appends /index.php/myLink
for all links when using nginx pretty urls, to fix
this we can use Nginx Helper Plugin, simply download
the plugin, install it, enable it, and that’s it! We now have pretty URLs.
If you didn’t install W3 Total Cache in the previous step, add this to your
/etc/nginx/sites-available/default
site configuration
# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
try_files $uri $uri/ /index.php?$args;
}
Have fun with your WordPress on steroids installation!