Install and Configure LEMP Stack (Linux, Nginx, MySQL & PHP) on Ubuntu 14.04

LEMP Stack is an archetypal model of web service solution stacks, named as an acronym of the names of its original four components: the Linux operating system, the Nginx(engine-x) HTTP Server, the MySQL relational database management system (RDBMS), and the PHP programming language. As a solution stack, LEMP is suitable for building dynamic web sites and web applications to increase the ability of the server to scale in response to demand.

I will be installing the required packages on Ubuntu 14.04 LTS (Trusty Tahr). In addition, I’ve had great success on using Digital Ocean‘s and Linode‘s Virtual Private Server, they are easy to use and can typically be setup in 55 seconds. I will assume you have your server all setup, and you are ready to begin at the command line, so lets get started.

  • Install Nginx

Nginx (pronounced as engine-x) is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server written by Igor Sysoev.

First lets make sure our server is all up-to-date. Enter the following command in your terminal:

sudo apt-get update

Now, install Nginx using this command:

sudo apt-get install nginx

Start Nginx service using the command:

sudo service nginx start
  • Test Nginx

Open up your web browser and navigate to http://ip-address or http://localhost. You will see a screen something like below.

  • Configure Nginx

Check the No. of CPU(s) in your server using this command:

lscpu

Now Open the file /etc/nginx/nginx.conf in any editor:

sudo nano /etc/nginx/nginx.conf

Adjust the worker_processes (i.e No. of CPU’s in your server). In my case it’s 1. So I set the worker_processes to 1.

worker_processes 1;

Restart Nginx service:

sudo service nginx restart

The default vhost is defined in the file /etc/nginx/sites-available/default. And the virtual hosts are defined in server {} containers – let’s modify it as follows:

Open the file /etc/nginx/sites-available/default in any editor.

sudo nano /etc/nginx/sites-available/default

Under the server section, set the server_name to FQDN (Fully Qualified Domain Name) or IP address as shown below. Make sure you’ve also added index.php, See the example below:

[…]
server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;
    root /usr/share/nginx/html;
    index index.php index.html index.htm;
    # Make site accessible from http://localhost/
    server_name 127.0.0.1 ;
[…]

Explanation:

    • listen 80; -> listen for ipv4
    • listen [::]:80 default_server ipv6only=on; -> listen for ipv6
    • root /var/www/html; -> document root directory.
    • server_name 127.0.0.1; -> Server FQDN

Now, scroll down further and find the section #location ~ \.php$. Uncomment and modify the following lines as shown below.

[…]
location ~ \.php$ {
    try_files $uri =404; -> Add this line
    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;
}
[…]

Explanation:

Here, I added an extra line try_files $uri =404; to prevent zero day exploits.

Save and close the file.

  • Test Nginx Configuration

Test the nginx configuration for any syntax errors using command:

sudo nginx -t

Sample output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Restart Nginx service:

sudo service nginx restart
  • Install MySQL

MySQL is a relational database management system (RDBMS) that runs as a server providing multi-user access to a number of databases.

sudo apt-get install mysql-server-5.6 mysql-client-5.6

During installation, you’ll be asked to setup the MySQL “root” user password. Enter the password and select Ok.

Re-enter the password.

MySQL is now installed.

  • Verify MySQL status

You can verify the MySQL server status using command:

sudo service mysql status

Sample output:

mysql start/running, process 980
  • Configure MySQL

We need to tell MySQL to generate the directory structure it needs to store its databases and information. We can do this by using this command:

sudo mysql_install_db

Next, you’ll have to run a simple security script that will prompt you to modify some insecure defaults in the terminal. Begin the script by using this command:

sudo mysql_secure_installation

You will need to enter the MySQL root password that you selected during installation.

Next, you will be ask if you want to change that password. If you are happy with your MySQL root password, type “N” for “no” and hit “ENTER”. Afterwards, you will be prompted to remove some test users and databases. You should just hit “ENTER” through these prompts to remove the unsafe default settings.

Once the script has been run, MySQL is ready to go.

  • Install PHP

PHP (recursive acronym for PHP: Hypertext Preprocessor) is a widely used open-source general purpose scripting language that is especially suited for web development and can be embedded into HTML.

Install PHP with following command:

sudo apt-get install php5 php5-fpm

Install PHP MySQL library with the following command:

For MySQL version 5.5 and below.

sudo apt-get install php5-mysql

For MySQL version 5.6.

sudo apt-get install php5-mysqlnd
  • Configure PHP

Open php.ini file in any editor:

sudo nano /etc/php5/fpm/php.ini

Find the line cgi.fix_pathinfo=1, uncomment it and change the value 1 to 0.

[…]
cgi.fix_pathinfo=0
[…]

Now restart PHP-FPM service.

sudo service php5-fpm restart

To check whether php5-fpm is running or not. Use the command below:

sudo service php5-fpm status

Sample output:

php5-fpm start/running, process 1124
  • Test PHP

Now create the following PHP file in the nginx document root folder /usr/share/nginx/html:

sudo nano /usr/share/nginx/html/info.php

Add the following lines in the file.

    phpinfo();
?>

Save and close the file.

Navigate to http://server-ip-address/info.php. It will display all the details about PHP such as version, modules, build date and commands, etc.

If this was successful, then your PHP is working as expected.

  • PHP-FPM Use A TCP Connection

By default PHP-FPM listens on the socket /var/run/php5-fpm.sock. If you want to make PHP-FPM use a TCP connection, open the file /etc/php5/fpm/pool.d/www.conf.

sudo nano /etc/php5/fpm/pool.d/www.conf

Modify the listen line to look as follows:

[…]
;listen = /var/run/php5-fpm.sock
listen = 127.0.0.1:9000
[…]

Explanation:

This will make PHP-FPM listen on port 9000 on the IP 127.0.0.1 (localhost). Make sure you use a port that is not in use on your system.

Save and close the file. Restart php5-fpm service.

sudo service php5-fpm restart

Now open and modify the nginx configuration file:

sudo nano /etc/nginx/sites-available/default

Find the line fastcgi_pass unix:/var/run/php5-fpm.sock; and change it to fastcgi_pass 127.0.0.1:9000; as shown below:

[…]
location ~ \.php$ {
try_files $uri =404;
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;
}
[…]

Save and close the file. Finally restart nginx service.

sudo service nginx restart
  • Install phpMyAdmin

phpMyAdmin is a free and open source tool written in PHP intended to handle the administration of MySQL with the use of a web browser. It can perform various tasks such as creating, modifying or deleting databases, tables, fields or rows; executing SQL statements; or managing users and permissions.

It is available in the Official Debian repositories. So install it with command:

sudo apt-get install phpmyadmin

Select the web server that should be automatically configured to run phpMyAdmin.

By default, nginx will not be displayed here. So, select apache or lighttpd, and we will configure phpmyadmin to work with nginx webserver later.

Note: When the first prompt appears, apache2 is highlighted, but not selected. If you do not hit “SPACE” to select Apache, the installer will not move the necessary files during installation. Hit “SPACE”, “TAB”, and then “ENTER” to select Apache2.

Select Yes to configure database for phpMyAdmin with dbconfig-common.

Enter password of the database’s administrative user (i.e MySQL root user password).

Enter MySQL application password for phpMyAdmin.

Re-enter the password.

phpMyAdmin has been successfully installed now.

Create a symbolic link between phpMyAdmin and the website root directory. Here our website root document directory is /usr/share/nginx/html/.

sudo ln -s /usr/share/phpmyadmin/ /usr/share/nginx/html/

Restart nginx server.

sudo service nginx restart
  • Access phpMyAdmin in Web Console

Now you can access the phpMyAdmin console by navigating to http://server-ip-address/phpmyadmin from your browser.

Enter your MySQL username and password which you have given in previous steps.

You will be redirected to phpMyAdmin main web interface. This is how my phpMyAdmin dashboard looks.

Now you can manage your MySQL databases from phpMyAdmin web interface.

  • Fix “The mcrypt extension is missing. Please check your PHP configuration.”

Install mcrypt extension for PHP with following command:

sudo apt-get install php5-mcrypt

Enable the mcrypt extension with following command:

sudo php5enmod mcrypt

Restart Nginx service:

sudo service nginx restart

Restart PHP-FPM service:

sudo service php5-fpm restart

With that, your phpMyAdmin installation is now operational.

  • Secure your phpMyAdmin Web Console

The phpMyAdmin web console installed on our server should be completely usable at this point. However, by installing a web interface, we have exposed our MySQL system to the outside world.

Even with the included authentication screen, this is quite a problem. Because of phpMyAdmin’s popularity combined with the large amount of data it provides access to, installations like these are common targets for attackers.

We will implement a simple strategy that I commonly use to lessen the chances of our installation being targeted and compromised. We will create an additional, web server-level authentication gateway that must be passed before even getting to the phpMyAdmin login screen.

  • Setting up a Web Server Authentication Gate

From this forward on, we will need to modify our Nginx configuration file again. And we have to create a password file that will store our authentication credentials. Nginx requires that passwords be encrypted so we will be using OpenSSL suite, which should already be installed on your server.

To create an encrypted password, use the following command:

openssl passwd

You will be prompted to enter and confirm the password that you wish to use. The utility will then display an encrypted version of the password that will look something like this:

j5ZOrrW0.X5pN

Copy this value, as you will need to paste it into the authentication file we will about to create.

Now, create an authentication file. We will call this file pma_pass and place it in the Nginx configuration directory:

sudo nano /etc/nginx/pma_pass

Within this file, you simply need to specify the username you would like to use, followed by a colon (:), followed by the encrypted version of your password you received from the openssl passwd utility.

We are going to name our user demo, but you should choose a different username. The content of this file will look like this:

demo:j5ZOrrW0.X5pN

Save and close the file when you are finished.

Now, we are ready to modify our Nginx configuration file. Open this file in your text editor to get started:

sudo nano /etc/nginx/sites-available/default

Within this file, we need to add a new location section. This will target the location we chose for our phpMyAdmin interface.

Create this section within the server block, but outside of any other blocks. We will put our new location block below the location / block, see the highlighted block for example:

[…]
server {
    […]
    location / {
        try_files $uri $uri/ =404;
    }
    location /phpmyadmin {
        auth_basic "Restricted Area! Authorized Penguin Only";
        auth_basic_user_file /etc/nginx/pma_pass;
    }
    […]
}
[…]

Within the highlighted block, as you can see—we need to set the value of a directive called auth_basic to an authentication message that our prompt will display to users. We do not want to indicate to unauthenticated users what we are protecting, so do not give specific details. We will just use “Restricted Area! Authorized Penguin Only” in our example.

We then need to use a directive called auth_basic_user_file to point our web server to the authentication file that we created. Nginx will prompt the user for authentication details and check that the inputted values match what it finds in the specified file.

Save and close the file when you are finished.

To implement our new authentication gate, we must restart the web server:

sudo service nginx restart

Now, if we visit our phpMyAdmin location in our web browser (you may have to clear your cache or use a different browser session if you have already been using phpMyAdmin), you should be prompted for the User Name and Password you added to the pma_pass file:

Once you enter your correct credentials, you will be taken to the default phpMyAdmin login page. This added layer of protection will help keep your MySQL logs clean of authentication attempts in addition to the added security benefit.

That’s it! Congratulation your LEMP Stack server is ready for use.

The fact that I don’t believe that I’m better than anyone else gives me an inevitable sense of superiority.