Server Management for Beginners Part 2: Installing Nginx, PHP and MySQL


This post is part 2 of the Server Management for Beginners series. In our last tutorial we went through the steps of provisioning and booting a brand spanking new VPS at Linode. We set the server’s hostname and timezone, added SSH access for a non-root user, and lastly, installed some base level security software.

Well that’s all good and all, but what we have now is a relatively secure, up-to-date, blank slate set up on someone else’s computer.

That doesn’t help us much, so let’s get some useful software on there! We’re going to install the requisite packages and software to run a PHP application (WordPress), but you could just as easily install Node.js, Ruby, or Python and their supporting packages.

What we’re going to do is set up Nginx, MySQL, and PHP or what some people who can’t spell call LEMP (Linux, Nginx, MySQL, PHP). A lot of this is covered in this awesome guide by Linode, but we’ll dig a little deeper into some of the gotchas and best practices.

Remember our little buddy Apt? It’s going to help us install the software we need. So let’s SSH back into our Linode VPS and get started.

Installing Nginx

We’ll start with setting up Nginx, as it’s one of the more complicated of the group. There is a great Getting Started guide on the Nginx site, it’s definitely worth reading if you’d like to dig a little deeper into the configuration options.

We’ll first make sure all our packages are up to date by running:

sudo apt-get update 

Then, we’ll run the following command:

sudo apt-get install nginx

You’ll be prompted to enter your password as you’ve logged in as your non-root user, right? Right.

Once you enter your password, hit ‘y’ to finish the installation.

Boom, now you’ve got Nginx running! If you set up your DNS in part one, you should be able to see the Nginx default page by loading up that URL in the browser:


Protip: if you didn’t set up DNS for your server, that’s ok too! You can use your machine’s host file (on macOS it’s /etc/hosts) to add a record that points to your server’s IP.

The next thing we should cover is where the configuration files for Nginx are kept. Like most Linux programs, the configuration files are stored under the /etc folder. You can think of /etc as the holding place for Linux configuration files. In Linux land almost everything has a text file with some configuration info in it. It’s one of the major differences between Linux and Windows.

So with this in mind, the Nginx configuration files can be found under /etc/nginx. Shocker right? In this folder there are a whole bunch of files and folders, but we’ll only cover a few in this post.

The most important configuration file for Nginx is the nginx.conf file. This bad boy contains all of the global configuration options for the server. Settings in here act as defaults and can be overridden in other configuration files loaded later.

Let’s open this file by running:

sudo nano nginx.conf

We’re going to leave most of these settings alone for now as Nginx is configured well out of the box. We will however, uncomment the server_tokens setting so that information about Nginx won’t be output in an HTTP response header. This is a security best practice. We’ll also add the following underneath the server_tokens directive to increase the max upload size:

client_max_body_size 64m;

Also take note of the include statements near the bottom:

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

This is what allows us to use what are called ‘Virtual Hosts’. Virtual Hosts or vhosts allow you to run multiple websites on the same server by separating out server definition rules into separate server blocks. You can add server blocks to nginx.conf, but the common approach is to define these site definitions in their own files – one for each site. On Ubuntu, these files are normally located in the /etc/nginx/sites-available folder.

Before we get into actually setting up a new server block, let’s save the nginx.conf file by hitting Ctrl-O and exit with Ctrl-X.

Next, we’ll need to create the directory for our new website to live in. As Ashley recommends, we’ll use our home folder to keep our websites, as it’s easy to remember. In this case I’ll create some folders by running the following commands:

mkdir /home/superman/sites
mkdir /home/superman/sites/
mkdir /home/superman/sites/

It’s good to use the FQDN (Fully Qualified Domain Name) as the folder name, as it’s easy to know what you’re looking at when you login. We’ll use the /home/superman/sites/ folder as our web root for our new site. Using ‘public’ as a folder name is a common practice to signify that this is a publicly accessible directory that can be accessed over the internet.

Remember that ‘Welcome to nginx’ page we loaded up earlier? It’s configured in the default site file, located at, you guessed it, /etc/nginx/sites-available/default. So let’s head to that sites-available folder and create a new server block file.

cd /etc/nginx/sites-available/

First of all, we’re going to delete that default server symlink (more on that later) so that it’s no longer loaded. To do this run:

sudo rm /etc/nginx/sites-enabled/default

Enter your password when prompted, and then restart Nginx to make sure that boring default page is gone:

sudo systemctl restart nginx

Protip: systemctl is a new Ubuntu utility that is used to interface with systemd. Think of it as a utility to manage services running on your server. You can use it to restart services, but also to check the status of services, along with a host of other things.

If you load up your domain in a browser now you shouldn’t see the default Nginx page any longer. That’s a good sign, don’t worry we’ll have something better in it’s place soon enough! Next up we’ll create a new site file for our awesomesauce-improved website. We’ll use our old friend Nano to create a new file, using your own domain name:

sudo nano

Once we’re in the Nano edit screen, copy and paste the following server definition block, replacing the file path and domain info with that of your own server:

server {
    listen 80;
    listen [::]:80;


    root /users/superman/sites/;
    index index.html index.php;

    location / {
        try_files $uri $uri/ /index.php?$args;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

In the above server block, you can see that there are 2 listen directives. The first is for IPv4 connections, and the second if for IPv6 connections. We won’t get into the difference here, as IPv6 is a huge topic for another day.

The 80 part denotes the port on which Nginx should listen. As you may know, port 80 is the default HTTP port, so by listening on it, we’re listening to all HTTP connections to our server.

Note for SSL connections (HTTPS) we would need to listen on port 443. Setting up SSL and the related certificates is a topic for another post.

The next part of the server block is the server_name declaration. This is the name can be any combination of wildcard strings, regular expressions or simple strings. The Nginx documentation covers all the possible variations that can be used, but to keep it simple we’ll use the straight up base URL we want to use for our server. In this case

The root declaration defines the file system path for where to find the files for this server block. Pretty straightforward.

The index declaration tells the server what kind of ‘index’ files should be loaded. As you may know, servers have special files that act as default ‘indexes’ for a directory. If no other file or path can be found, the index file is loaded. Here we list the file names that should be treated as indexes.

The next section of this server block is the location declaration. A location block lives within a server block and defines how specific URLs or resources (like images, or files with a specific extension) should be handled. What the location declaration in this server block does is check to see if the $uri exists, if it doesn’t Nginx tries to see if the $uri/ with a slash exists. If neither exists, it attempts to find the URL based on the args passed to index.php. This is necessary for WordPress’ pretty permalinks.

The last location block is all about PHP. We haven’t installed PHP yet (spoiler: that’s the next step) but we’ll add the declaration here so we don’t have to come back and edit this file later. We’ll go over what this block covers after we install PHP.

And that’s it for now! We now have an Nginx server block ready to serve up content. To enable this website we’ll save this file in Nano by hitting Ctrl+O. The last step is to create a symbolic link or symlink to the /etc/nginx/sites-enabled folder. We can do this by running the following command:

sudo ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

The ln command is the Linux ‘link’ command. This is a way to match a name to a file on the file system. The -s makes a symbolic link, which essentially creates a ‘shortcut’ to an existing file.

Next we’ll want to make sure our configuration changes are all good and there are no errors with the configuration. This can save us some headaches down the line. We’ll use the -t flag on the nginxcommand, like so:

sudo nginx -t

If there are any errors, the output of the above command will tell you where and how to fix it. Otherwise, if the configuration is all good, you just need to restart Nginx to apply the changes. To do that run:

sudo systemctl restart nginx


The last thing we’ll cover with regards to Nginx is where the logs are stored. Nginx, like many systems on Linux has two main types of log. The error.log is just that, a log of all Nginx server errors. Anytime you see a 503, 502, 404 etc. the specifics of the error will be logged to this file. The access.log is a log of all network traffic that Nginx receives. This is useful for debugging network errors or looking for malicious traffic against your server.

Nginx has a configuration option to enable a custom logging directory, but I normally stick to the default log destination under /var/log/nginx as it’s easy to remember and the logs are already under rotation with the logrotate utility. This means that when log files get too large, or too old, they are automatically removed and archived.

Nginx’s default error log is located at /var/log/nginx/error.log and the access log is located at /var/log/nginx/access.log.

To inspect logs while Nginx is running, a great tool to use is tail. Tail allows you to read the last n lines of a file. With the -f (follow) flag, you can also watch a file as it’s written to. The -n flag let’s you control how many lines are output – 100 is the default but you can dial that down if you like.

To read and watch the last 100 lines of the Nginx error log, run the following:

sudo tail -f -n 100 /var/log/nginx/error.log

And you’ll get something like:


If you want to exit tail, just hit Ctrl-C.

Installing and Configuring PHP

Ok let’s take a quick breather – grab a coffee or beer if it’s that time of day where you are.

Cheers! Next up we’re going to install PHP. Since we’re running Ubuntu 16.04, the latest PHP binaries are in the official apt repository, so we don’t need to do anything fancy to install the latest and greatest version – PHP 7.

To install PHP, run the following:

sudo apt-get install php7.0-cli php7.0-fpm

Hit ‘y’ when prompted and let the installer finish. What that did was install a few PHP packages. php7.0-cli is the CLI version of PHP. php7.0-fpm is required to run PHP with Nginx.

If you’re looking for the php.ini files down the line, there are two separate files: one for the CLI version and one for the FPM version . They are located at /etc/php/7.0/fpm/php.ini and /etc/php/7.0/cli/php.ini respectively.


One of the major differences between the Apache web server and Nginx is how Nginx handles PHP. In most setups Apache uses the mod_php extension to handle PHP processes directly. In Nginx, a separate process is required to handle PHP requests. This separate process is called php-fpm or ‘FastCGI Process Manager‘ and allows Nginx to operate through a process called FastCGI proxying. All we really need to know is that there are two processes that run our PHP code: php-fpm is the PHP interpreter and Nginx is the proxy that passes HTTP requests to our PHP process and back again.

The reasoning for all this is that it separates concerns. Nginx acts only as a web server. Nginx is really good at serving static assets (like HTML, images and CSS) and by offloading the PHP handling to a separate process, it allows us to use programs designed specifically for each task.

Getting back to our server, we’ve now installed all the various components to run PHP but there’s a little more configuration required to run PHP files through Nginx.

Remember that server block we just created? There’s that additional location directive we added beforehand. Let’s jump back in there and go over those settings.

sudo nano /etc/nginx/sites-available/

Looking at this server block again, we have the following location block:

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

The location ~ \.php$ part simply means any URL that ends in .php will run the following rules. The include snippets/fastcgi-php.conf; line includes a file located at /etc/nginx/snippets/fastcgi-php.conf. This file includes further FastCGI config rules to be used.

Similarly, the include fastcgi_params; includes a file at /etc/nginx/fastcgi_params that sets a bunch of FastCGI default parameters. These params are used to pass information (like HTTP headers and query strings) between the php-fpm process and Nginx.

The next directive – fastcgi_pass unix:/run/php/php7.0-fpm.sock; is what tells the PHP process what server to proxy through. In this case we’re using a Unix socket, but some setups use the local IP ( This is perfectly acceptable as well.

The last directive is a FastCGI param that sets the $SCRIPT_FILENAME variable to the PHP filepath used in the current request.

Moar Extensions

The next thing we’ll install for PHP is some other extensions we’ll need for WordPress. We’ll install the GD image processing library, cURL for PHP, PHP XML libraries and the zip module by running the following command:

sudo apt-get install php7.0-gd php7.0-curl php7.0-xml php7.0-zip

File Permissions

The last thing we’ll do is update the file permissions for php-fpm so that PHP runs as our superman user. This will hopefully prevent any file permissions headaches later on. We’ll run the following command to open the php-fpm www.conffile:

sudo nano /etc/php/7.0/fpm/pool.d/www.conf

In this file we’ll change the user and group variables to superman and save the file:

user = superman
group = superman

After this we’ll need to restart the php-fpm service for the changes to take effect. We’ll do this by running:

sudo systemctl restart php7.0-fpm

Woo! Alright so now that we’ve set the correct file permissions and we’ve installed PHP and Nginx, let’s give this baby a whirl. Let’s create a test PHP script to make sure everything is working:

nano /home/superman/sites/public/test.php

In the file, let’s add the good ol’ phpinfo(); command to see what we’ve got running.

Awesome! We’ve got Nginx and PHP running up on our VPS now 👏!

Installing MySQL

The last thing we’ll need to install is MySQL and the accompanying PHP extension. To do that run:

sudo apt-get install mysql-server php7.0-mysql

This install is a little bit different, as the MySQL installation requires a little bit of user interaction. Enter your root password when prompted and hit enter to continue with the installation.


Alright, now let’s login to our MySQL server as our root user:

mysql -u root -p

The -u flag tells MySQL which user to login with, in this case root and the -p flag tells MySQL to ask for a password when logging in.

We’ll then create a user and database for our WordPress install to use. We’ll use the following script to do just that:

CREATE DATABASE superman_wp;
CREATE USER 'superman_db_user' IDENTIFIED BY 'hY5fsfBzJyFF';
GRANT ALL PRIVILEGES ON superman_wp.* TO 'superman_db_user';

This script is pretty self-explanatory, as SQL is pretty easy to understand. What we’re doing here is creating a database called superman_wp and creating a new user named superman_db_user while setting this user’s password with the IDENTIFIED BY clause. Make sure to replace the password with a strong version of your own. We then give ALL privileges (select, delete, update, alter etc.) to the superman_db_user only on the superman_wp database. We finish up by flushing the privileges so they take effect right away, and quit the MySQL interface.

That go ok? Great! Now we have a database setup for WordPress. But accessing MySQL can be cumbersome, so we’ll use a program to check on our new database.

Protip: If you ever need to reset a MySQL user’s password, you can run the follow SQL command to reset the password in MySQL 5.7.6+:
SET PASSWORD FOR 'superman_db_user' = PASSWORD('newpassword');

On macOS/OSX I use the free Sequel Pro, and on Windows there’s SQLYog Community, which is similar.

To login with Sequel Pro, you’ll need to use the SSH option. Fill in the fields and for the SSH password, use your private key file (id_rsa) for authentication.


So now that we can login with our WP user, we’re pretty much good to install WordPress. Look at us go! For the purposes of this tutorial I’m going to use WP-CLI to install WordPress. It’s a great utility to have around anyway.

We’ll follow the installation instructions on their website, as they’re pretty straight forward. Once that’s done, let’s change into our site root and install WordPress:

cd /home/superman/sites/
wp core download
wp core config --dbname=superman_wp --dbuser=superman_db_user --dbpass=hY5fsfBzJyFF
wp core install --title="Superman's blog" --admin_user=site_admin --admin_password=strongpassword

Here we change into our web root, and use the wp core download command to get the WordPress core files. The wp core config command creates the base wp-config.php file with our database settings and the following wp core install command installs WordPress for us. Pretty nifty right?

Next Steps

And that’s it! What a whirlwind, but now we’ve got our own VPS running Nginx, PHP, and MySQL, serving up a brand new WordPress installation. Not bad, not bad at all.

While this is a working WordPress server, there are a few more things we could do to make it faster and more functional. In the next part of this series I’ll go over setting up email sending, WordPress caching, SSL, and some tweaks for further Nginx configuration.

Have you ever set up a server with Nginx, PHP and MySQL? Did you come across any setbacks or gotchas? What else would you like to see covered from a beginners perspective? Let us know in the comments!

About the Author

Peter Tasker

Peter is a PHP and JavaScript developer from Ottawa, Ontario, Canada. In a previous life he worked for marketing and public relations agencies. Love's WordPress, dislikes FTP.