Wordpress is 22% of the internet, that is insane. It’s so popular because it makes it so easy for a layman to make a website. The problem is hosting and maintanence, there just doesn’t seem to be any sort of complete (at least to my standards) guide on how to do it right. This my guide on how to host a multi-site wordpress installation.

TLDR: Use Digital Ocean to spin up a Debian Jessie instance, it has PHP 5.6 as the default PHP version in its repos. PHP 5.5 (ubuntu 14.04 default) is no longer supported and the wordpress ecosystem is not yet ready for PHP 7 (ubuntu 16.04 default).

Rationale

I want an easy, hassle free way to deploy and manage wordpress sites. I’m also very opinionated about what software I run (wait, what!? there is more than one combination to host Wordpress).

I’ve tried to run Wordpress on hhvm before, facebooks JIT php implemenation, but it just doesn’t make sense to wordpress on it. The VM has a warm-up period, so to get any of the benifits of using it, you need to keep to keep it hot. Which is not a problem when you’re running high volume, long-running processes. But when your running low volume website, there is really very little benefit to running it. Then there is php7 which in benchmarks has been showing smilar performance metrics. So great we’re going to run php7? Not so fast, even though its been out for almost a year, the Wordpress ecosystem is just not ready for it yet. Too plugins and themes just fall over when run on php7. That leaves us with php5.6, which is currently in an extended security support period, until the end of 2018.

Next we need a distro to that has our php version as the default version in the repos. I’m in the business of jumping through hoops to get the right version of php installed. My first instincts were to use an LTS version of Ubuntu, unfortunately neither LTS versions come with the correct version. 14.04 comes with php5.5 and 16.04 comes with php7. So what distro does php5.6 come as the default version… good ol’ Debian Jessie.

The final 2 pieces we need are a webserver and and database, thankfully these were a lot more straight forward descisions. For the webserver the obvious choice is nginx in front of php-fpm, it’s fast, lightweight and pretty much unstoppable. The final component, the database, MariaDB is an open-source fork of MySQL by one of the original authors of MySQL. It’s a binary drop in replacement for MySQL with some additional features and performance tweeks.

What about docker?

Docker is amazing! Like really, really amazing. But it is very opinionated and unfortunately a lot of those opinions don’t work well with Wordpress. One of the core opinions about running docker is that you only have one process per container. This is a problem for me because I want to run Wordpress on php-fpm behind nginx and these two components need to share state. Now you could run each process in spereate containers and mount a shared volume into both of them. The only problem is that Wordpress will start to get file permission errors. So all your file uploads will fail and you won’t be able to install any updates. This is not very practical.

Another aproach would be to violate the single process per container principle and run it all in a single container. But then whats the point of running it in a container, why not just use a small virtual machine.

Every tool has it’s purpose, Docker is not a good tool for running Wordpress. Both applications make assuptions about the environemtn that they will be running in. Unfortunately those assuptions just don’t gel well together.

Why try smash a square peg into a round hole.

Setting it all up

Provisioning a server

First things first let’s head over to Digital Ocean and provision a Debian 8.0 server. Once you’ve sshed onto the server (I’m going to assume you’ve got ssh keys setup) let’s lock down all unecessary ports:

$ ufw allow 22
$ ufw allow 80
$ ufw allow 443
$ ufw enable

Now let’s first install nginx, php is still very attached to the apache webserver and it stills comes with the default php package in the Debian repostitories. If you don’t already have nginx running on the server, apache will start to running when you install php, you’ll then get an error when installing nginx. Hence the first bit of software that we’re putting on the server is nginx:

$ apt-get install nginx

Next we can install php:

$ apt-get install php5

Finally we need to install our database:

$ sudo apt-get install software-properties-common
$ sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db
$ sudo add-apt-repository 'deb [arch=amd64,i386] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.1/debian jessie main'
$ sudo apt-get update
$ sudo apt-get install mariadb-server

During the installation process you’ll be asked to create a password for the root user.

Finally let’s install wp-cli to make it easier to administer our WordPress blogs via the cli:

$ curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
$ php wp-cli.phar --info
$ chmod +x wp-cli.phar
$ sudo mv wp-cli.phar /usr/local/bin/wp

Configuring WordPress

Now let’s install WordPress:

$ cd /var/www
$ wget https://www.wordpress.org/latest.tar.gz
$ tar -xvf latest.tar.gz
$ cd wordpress

Now lets configure a Database for our WordPress site to use. Digital Ocean has some great tutorials on basic MySQL admin:

Once you’ve setup the database you’ll need to tell WordPress about it, edit your wp-config.php:

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'db_name';

/** MySQL database username */
define('DB_USER', 'db_user');

/** MySQL database password */
define('DB_PASSWORD', 'db_pass');

/** MySQL hostname */
define('DB_HOST', localhost);

Make sure the www-data user can edit all the files in the wordpress directory, otherwise you’re not going to be able to install any plugins:

$ chown -R www-data:www-data /var/www/wordpress

Finally let’s configure Nginx to send traffic to WordPress:

$ nano /etc/nginx/conf.d/wordpress.conf

upstream php {
    server unix:/var/run/php5-fpm.sock;
}

server {
    ## Your website name goes here.
    server_name domain.tld;
    ## Your only path reference.
    root /var/www/wordpress;
    ## This should be in your http block and if it is, it's not needed here.
    index index.php;

    access_log /var/log/nginx/wordpress-access.log;
    error_log /var/log/nginx/wordpress-error.log;

    location = /favicon.ico {
            log_not_found off;
            access_log off;
    }

    location = /robots.txt {
            allow all;
            log_not_found off;
            access_log off;
    }

    location / {
            # This is cool because no php is touched for static content.
            # include the "?$args" part so non-default permalinks doesn't break when using query string
            try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
            #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
            include fastcgi.conf;
            fastcgi_intercept_errors on;
            fastcgi_pass php;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
            expires max;
            log_not_found off;
    }
}

Now visit your domain and finish the installation.