How to: Set-up and configure a Web Server running Nginx and PHP-FPM

An installation how to guide from Stock Ubuntu 16.04 to Web Server (PHP-FPM, Nginx, Git, Varnish and AWS Logs).

This is a quick guide on going from a stock Ubuntu 16.04 server to a ready to go web server, with PHP-FPM, Nginx, Git, Varnish and AWS Logs. If you find this guide useful please take a moment to say so in the comments.

This guide doesn't cover in detail the configuration of either Nginx or Varnish as these are subjects that could easily warrant their own, separate guides.

Stating the obvious, but at the time of writing these steps worked, obviously things change so I can't guarantee these steps will still work in the future.

To start with I selected the Ubuntu 16.04 HVM 64-bit AMI at Amazon Web Services (AWS). These steps can be followed on the smallest (free tier eligible) instance size, currently the t2.micro instance.

Back to topConfigure the server timezone

Run this simple wizard to configure the server timezone:

dpkg-reconfigure tzdata

Back to topInstalling Nginx and PHP-FPM

So connect to your new instance using SSH.

First we need to add some PPA repositories so when we install Nginx and PHP we're getting the very latest versions:

add-apt-repository ppa:nginx/stable
add-apt-repository ppa:ondrej/php

Download the latest package lists from the repositories:

apt-get update

Upgrade any packages that can be:

apt-get upgrade

Any upgrades that are kept back can be installed as follows:

apt-get dist-upgrade

Install Nginx and then PHP-FPM

apt-get install nginx
apt-get install php7.2-fpm

Back to topInstall PHP Extensions

You might have particular PHP extensions you need but here's a starting point:

apt-get install php7.2-curl php7.2-gd php7.2-mcrypt php7.2-xmlrpc php7.2-mbstring

Now is also a good time to install ImageMagick if you need it.

Personal preference but I usually run PHP under a different user than the default www-data user.

Next, create a user under which PHP-FPM will run:

useradd worker

Back to topLogfile storage

Create the logs storage folder

Create the /logs folder, we'll point the Nginx and PHP logs at this folder:

mkdir /logs

In the /etc/nginx/nginx.conf file set:

error_log /logs/error.log warn;

Back to topSetting up a website

Create a directory where your websites will be stored:

mkdir /websites

Create a folder for your (first) website:

cd /websites
mkdir example.com

Set the ownership of the websites directory and it's sub-directories to the "worker" user we created:

chown -R worker:worker /websites

Back to topInstalling Git

Install Git

apt-get install git-core

Create a key for use with Git:

cd ~

When you run the next command you can just press enter repeatedly to accept the defaults, if you don't want/need to supply a passphrase:

ssh-keygen -t rsa

To output the public key so you can copy and paste it easily run:

cat ~/.ssh/id_rsa.pub

Copy and paste the public key into your Git service.

Back to topClone the website's files from a Git repository

Clone the website's files into this directory (the Github URL below doesn't actually work, it's just an example for you to replace with the correct path to your Git repo):

cd /websites/example.com
git clone git@github.com:enovatedesign/example.git .

Now's a good time to grant any particular write permissions your website may require.

Back to topSet-up the Nginx virtual host

The next step is to create the Nginx virtual hosts file. I'm not going to cover Nginx configuration at this point as that warrants a guide of its own.

Create/duplicate an existing Nginx configuration file for the new site:

These steps simply copy the default virtual hosts file that comes with a fresh Nginx installation and opens the new file in the Nano text editor for you to tweak:

cd /etc/nginx/sites-available
cp default example.com
nano example.com

Once the virtual hosts configuration file is complete we need to symlink it to the "sites-enabled" folder:

ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com

Next, we can test the configuration to make sure everything is present and correct:

nginx -t

All going well, we can ask Nginx to reload its configuration so it picks up the newly enabled virtual hosts file:

service nginx reload

Back to topConfigure PHP-FPM

Next we need to configure PHP to run as the user we added:

Edit the /etc/php/7.1/fpm/pool.d/www.conf file, changing the lines so they read as follows:

user = worker
group = worker

listen.owner = worker
listen.group = worker

Then restart the PHP-FPM service:

service php7.1-fpm restart

PHP should now be working correctly. If it isn't then check your Nginx log file for any errors.

It's recommended to make a few edits to the php.ini as required, I usually make a point of editing the following lines:

Please note: Comment out line 3 as shown so the default kicks in or replace it with the default's value.

max_execution_time = 300
memory_limit = 256M
;error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
error_log = /logs/php_errors.log
upload_max_filesize = 8M
date.timezone = Europe/London

Back to topSet-up AWS Logs

This is worth doing as it allows you to save the server's logs into AWS CloudWatch, which then allows you to retain your logs even if you terminate the instance. It also provides the added benefit of being able to set-up log metrics, which can then trigger alarms and in turn notifications, such as when a certain number of 404 or 500 HTTP status codes are returned within a certain period of time.

I've covered this in more detail in the recent AWS CLoudWatch Logs blog post.

Back to topInstall Varnish (optional)

Varnish is a fantastic piece of software, it caches your web pages to RAM so requests are served at lightning speed straight from RAM without even touching the disk. Even with a t2.micro instance you should be able to achieve response times close to 200ms (as reported by Pingdom from multiple locations worldwide)

Again, the Varnish documentation has this well covered, but I have included the steps below too (note the change from "precise" to "trusty"):

The steps are:

curl https://repo.varnish-cache.org/ubuntu/GPG-key.txt | apt-key add -
echo "deb https://repo.varnish-cache.org/ubuntu/ trusty varnish-4.0" >> /etc/apt/sources.list.d/varnish-cache.list
apt-get update
apt-get install varnish

This will install and start the Varnish service.

Back to topConfigure Varnish

We currently have Nginx running on port 80 so we need to swap things around so Nginx will form the back-end server for Varnish running on port 8080 and Varnish will run on port 80. This also allows us to request websites on port 8080 if we ever need to try to debug something to see what our back-end server (Nginx in this case) is serving to Varnish.

First stop the Nginx service:

service nginx stop

Edit the file /etc/default/varnish

Switch Varnish to port 80 by changing the port reference here:

DAEMON_OPTS="-a :6081 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-s malloc,256m"

To 80, thus:

DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-s malloc,256m"

Restart Varnish:

service varnish restart

Next you need to edit the Nginx virtual hosts file to change it from listening on port 80 to port 8080 instead:

nano /etc/nginx/sites-available/example.com

Start Nginx:

service nginx start

Next we need to configure Varnish to use Nginx (which is now running on port 8080) as the back-end server.

Again, covering the configuration of Varnish via VCL files is beyond the scope of this guide so I have simply included the code necessary to define a basic Nginx back-end server running on the same server on port 8080.

So we need to edit the file /etc/varnish/default.vcl to include the back-end server definition:

backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

Back to topTesting

To test the new instance I normally adjust my computer's hosts file to point a domain name that matches an Nginx virtual host through to the server.

You should be able to request the domain with the port number to compare the direct output from Nginx to that served by Varnish:

  • http://www.example.com:8080/ (Nginx)
  • http://www.example.com/ (Varnish)

You should be able to identify if Varnish is running by inspecting the headers. Requests served from Varnish normally include an "Age" header that stores the time in seconds since that item was cached.

If you've found this guide useful please let me know in the comments.

You might also like...

Why Hosting Matters

Dan Walsh by Dan Walsh