LowEndBox - Cheap VPS, Hosting and Dedicated Server Deals

LEMP Stack Installation on Ubuntu 16.04

This tutorial will show you how to install and configure the LEMP stack on your VPS/LowEndBox.

LEMP stands for Linux Nginx MySQL PHP, and is a lightweight variation of the LAMP stack, which includes Apache instead of Nginx. You’ll notice that it’s not written as LNMP, and that’s due to the pronunciation of the word Nginx (Engine-X).

LEMP is more suitable for Low End Boxes and cheap VPS machines, because it consumes far less resources than the LAMP stack (even under heavy load) and is 2.5 times faster according to benchmarks. We are going to install MariaDB instead of MySQL as it is much faster, but still fully compatible with the original MySQL.

What We Are Going To Cover

  • Installing Nginx
  • Nginx configuration basics
  • Setting up a Firewall
  • Installing and configuring MariaDB
  • Installing latest PHP
  • Configuring Nginx to serve PHP files
  • Testing the installed stack
  • (With a domain) Securing the site with Let’s Encrypt certificates


We will install and set up the LEMP stack on Ubuntu 16.04:

  • Starting with a clean VPS with
  • At least 512Mb of RAM
  • 15Gb of free disk space
  • You will need root user access
  • You must have DNS records for your your domain already set up to point to your VPS in order to install Let’s Encrypt certificates. If you don’t have one, access your IP address instead and skip the last step

Step 1: Install Nginx

Installation and Firewall Setup

First, log in to your server as root or as an another account with sudo privileges. Then, update your package manager’s cache:

sudo apt update

Install Nginx by running the following command:

sudo apt install nginx -y

You now have Nginx installed. Navigate to your domain (or IP address if you don’t have one) in your browser. You’ll see the following:

Nginx default page

which means that Nginx is started and works correctly. To make it start at every server boot, enable it via systemctl:

systemctl enable nginx

Every Ubuntu installation comes with the ufw (uncomplicated firewall) software preinstalled. For maximum security, you’ll now configure it to allow HTTP, HTTPS, FTP and SSH connections, and to deny all others:

sudo ufw allow OpenSSH
sudo ufw allow SSH
sudo ufw allow FTP
sudo ufw allow 'Nginx HTTP'
sudo ufw allow 'Nginx HTTPS'

Make it active (enable it) by running:

sudo ufw enable

Answer y when asked, and refresh the site in your browser. If you still see the same welcome page, you have configured your firewall correctly. If it shows an error (such as Access Denied), disable the firewall:

sudo ufw disable

and check its configuration:

sudo ufw status

You’ll see a list of enabled profiles. If you don’t see the ones from above, rerun the commands and enable it again.

Configuration Directories

On Ubuntu 16.04, Nginx stores its configuration files under /etc/nginx, and the default directory for serving static files is /usr/share/nginx/html. This will become important in the later steps, when we’ll need to change the configuration.

Step 2: Installing MariaDB

Install MariaDB and its client:

sudo apt install mariadb-server mariadb-client -y

When the installation finishes, start it:

sudo systemctl start mysql

You’ll notice that MariaDB responds to mysql commands. This is by design, to retain full compatibility with the original MySQL product.

Next, enable it via systemctl so it will automatically start on every server boot:

sudo systemctl enable mysql

After MariaDB installation, it is mandatory to run a script (called mysqlsecureinstallation) to secure the database:

sudo mysql_secure_installation

When it asks you for the current root password, press Enter, because that password has never been set. Then, enter a new password for the root user and answer y for anonymous user removal. If you plan on accessing the database from outside the server, answer n to the next question, otherwise type in y. Enter y for all questions that follow.

Try connecting with:

sudo mysql -u root -p

When prompted, enter the root password you just set.

You’ll see the mysql prompt:

mysql command prompt

This means that MariaDB is installed and properly secured. Type in exit to quit the console.

Step 3: Installing PHP

You’ll now install PHP 7.3, which was the latest at the time of writing. Install the following package so you’ll be able to manage apt repositories:

sudo apt install software-properties-common

Then, add the ondrej PPA, which hosts latest PHP versions:

sudo add-apt-repository ppa:ondrej/php

Press Enter when prompted.

Pull in information about new packages:

sudo apt update

To install PHP 7.3, run:

sudo apt install php7.3 php7.3-fpm -y

The reason for specifying php7.3-fpm for installation is that the Apache web server will too get installed if it’s omitted.

Then install additional PHP extensions that you will most likely need:

sudo apt install php7.3-mysql php7.3-mbstring php7.3-dev php7.3-gd php-pear php7.3-zip php7.3-xml php7.3-curl -y

Set PHP 7.3 you just installed as the default version:

sudo update-alternatives --set php /usr/bin/php7.3

Check the version of installed PHP:

sudo php --version

You’ll see that the version is 7.3, as it should be.

PHP version output

Step 4: Configuring Nginx to Serve PHP Content

PHP is installed and working correctly, but Nginx won’t automatically use it, meaning that we will have to configure Nginx on our own. Before we do that, we’ll fix a very insecure PHP settings called cgi.fix_pathinfo, which orders PHP to execute files that look like requested ones. By default it is set to 1 (which means enabled). If enabled, it allows bad actors to run malicious code on your server.

Open the PHP config file for editing:

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

Find the line


Replace it with


then save and close the file. Note that two changes had to take place – removing the semicolon to uncomment the line, as well as changing 1 to 0.

Restart PHP for the changes to take effect:

sudo systemctl restart php7.3-fpm

With this you have mitigated a glaring security hole. We’ll now configure Nginx to utilise PHP.

As noted in Step 1, Nginx stores its configuration files under /etc/nginx. Configuration that is currently enabled (which will be applied by Nginx) is stored in a folder named sites-enabled. Similarly, sites that are available, but are not enabled (and thus can not be accessed from the Internet), are stored in a folder named sites-available. We’ll edit the configuration of the default enabled site.

Delete the default config file, because we won’t be needing it:

rm /etc/nginx/sites-enabled/default

A copy of the default site config can be found at /etc/nginx/sites-available/default.

Create a new config file for editing:

nano /etc/nginx/sites-enabled/default

Paste in the following:

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

    root /var/www/html;

    index index.php index.html index.htm index.nginx-debian.html;

    server_name _;

    location / {
            try_files $uri $uri/ =404;

    location ~ \.php$ {
           include snippets/fastcgi-php.conf;
           fastcgi_pass unix:/run/php/php7.3-fpm.sock;

    location ~ /\.ht {
           deny all;

Save and close the file.

The first part of the above config specifies that Nginx should listen to the HTTP port 80 for requests. Then it sets the root folder for serving files to /var/www/html (which is where you’ll store your website files), and tells Nginx to first serve index.php (if it exists). If it does not exist, it falls back to .html files, as usual. The effect of this is that it will always try to first execute PHP code, and HTML only after.

The rest of the config shown tells Nginx how to call PHP when requested, and to deny access to .htaccess and similar files, which originate from the Apache web server and should not be served to users, despite not being used by Nginx.

Restart Nginx for the changes to take effect:

sudo systemctl restart nginx

Try reloading your site (or IP address) in your browser. You should see the same Nginx default welcome page, as before.

To test if PHP rendering is working, create a PHP file in /var/www/html:

sudo nano /var/www/html/index.php

Put the following into it:




Save and close the file.

The file shown above is a PHP script that calls the phpinfo() function, which will output very detailed information about the PHP installation.

Reload the site in your browser. This time, if all is well, you’ll see a page similar to this:

phpinfo() output

If you see a 502 Bad Gateway error, check the fastcgi_pass line in the above config (the PHP version may be different).

Be sure to remove the file immediately afterwards, because it reveals a lot of sensitive info:

rm /var/www/html/index.php

Step 5: Securing Your Domain Using Let’s Encrypt

If you have a domain name, fully registered and pointed to your server, you can secure it using free Let’s Encrypt TLS certificates, so you’ll have HTTPS access and a green padlock will show next to your domain name in all browsers.

Let’s Encrypt certificates will be generated and configured using Certbot, a free program created for this purpose.

To install Certbot, first add its PPA and the universe PPA:

sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt update

Then, install Certbot:

sudo apt install certbot python-certbot-nginx -y

Once it is done, run Certbot:

sudo certbot --nginx --rsa-key-size 4096

Enter an email address you know you’ll check, then agree to the terms of service by typing in A. Press Y if you want to subscribe to EFF’s newsletter, and then input your domain name. Certbot will then ask you if you wish to redirect all traffic to HTTPS – you certainly do, so type in 2.

You’ll receive a congratulatory message, which means that your domain is now secured with free HTTPS certificates from Let’s Encrypt. Refresh your domain in your browser one last time and observe the green padlock left of the site address, which means that the connection to your site is encrypted. If you want, you can run your site through the SSL Server Test and see that it will land you high marks.

Let’s Encrypt certificates expire after 90 days, and Certbot will automatically renew them when it decides it is time (every 60 days by default), so you don’t have to worry about them at all.

What You Can Do Next

You have now installed and configured a LEMP stack on your server. You can now host your PHP website(s) on it, and they will have access to a relational database (MariaDB). For example, you can install WordPress, Joomla, Drupal, Moodle, or any other PHP application that may need to access the database.

Dusko Savic is a technical writer and programmer.


1 Comment

  1. Alex:

    Thanks for your tutorial, very helpful.

    August 1, 2019 @ 8:58 pm | Reply

Leave a Reply

Some notes on commenting on LowEndBox:

  • Do not use LowEndBox for support issues. Go to your hosting provider and issue a ticket there. Coming here saying "my VPS is down, what do I do?!" will only have your comments removed.
  • Akismet is used for spam detection. Some comments may be held temporarily for manual approval.
  • Use <pre>...</pre> to quote the output from your terminal/console, or consider using a pastebin service.

Your email address will not be published. Required fields are marked *