Article

Table of Contents
Theme:
Was this article helpful?

2  out of  2 found this helpful

Try Vultr Today with

$50 Free on Us!

Want to contribute?

You could earn up to $300 by adding new articles!

Install a LEMP Stack on Ubuntu 20.04 LTS

Last Updated: Fri, Sep 25, 2020
MySQL and MariaDB PHP Ubuntu Web Servers

Introduction

LEMP (Linux, Nginx, MySQL, PHP) is a variation of the LAMP stack (Linux, Apache, MySQL, PHP). The only difference being LEMP uses Nginx, where LAMP uses Apache. Nginx is much faster, and generally more secure than Apache. In this guide, we will be configuring it to work with Let's Encrypt, which is a global Certificate Authority. They provide a free service for SSL/TLS certificates, as they are nonprofit. If you use Vultr DNS, we have a version of this article with a helper script that automates many steps.

1. Deploy Ubuntu Server

Change to your sudo user for the remaining steps.

2. Install Nginx and MariaDB

Update sources and install Nginx and MariaDB.

$ sudo apt update && sudo apt install -y nginx mariadb-server

3. Configure MariaDB

Run the first time setup for the MySQL installation. By default, it will ask for a root password, which is unset, so press enter.

Then it will ask if you want to set a root password, press N, and ENTER. For the rest of the prompts, press enter to accept the defaults.

$ sudo mysql_secure_installation

Connect to the MariaDB monitor.

$ sudo mariadb

Create a new test database.

CREATE DATABASE example_db;

Grant privileges for a non-root user. Replace the username and password with your current username, and a secure password of your choosing.

GRANT ALL ON example_db.* TO 'username'@'localhost' IDENTIFIED BY 'password' WITH GRANT OPTION;

Flush privileges and exit.

FLUSH PRIVILEGES;
exit

Now connect again with the username you just created. It will prompt for the password.

$ mariadb -u username -p

Make sure the example_db is accessible.

SHOW DATABASES;

Then set it as the current database.

USE example_db;

Create a table for later testing and exit.

CREATE TABLE table1(column1 varchar(255));
INSERT INTO table1 VALUES("Database connection established successfully");
exit

4. Install PHP and Certbot

Install php-fpm and php-mysql.

$ sudo apt install -y php-fpm php-mysql

Install snap dependencies.

$ sudo apt install -y snap

With snap, install certbot. This ensures the correct version is installed.

$ sudo snap install --classic certbot

5. Configure Nginx

Make a new directory for the website. Replace example.com with your domain

$ sudo mkdir -p /var/www/example.com

Give the correct user permissions to the website directory. Replace example.com with your domain.

$ sudo chown -R $USER:$USER /var/www/example.com

Open a new file in the nginx sites-available directory. Replace example.com with your domain.

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

Add the following code snippet, save, and exit. Make sure to change all instances of example.com with your domain.

server {
    listen 80;
    listen [::]:80;
    server_name www.example.com;
    server_name example.com;
    root /var/www/example.com;
    location / {
        index index.html;
    }
}

Restart ngninx.

$ sudo systemctl restart nginx.service

Run a certbot dry run. It will prompt you for some information. Replace both instances of example.com with your domain.

$ sudo certbot certonly --dry-run --nginx -d example.com,www.example.com

It should report the dry run ran successfully. Now obtain the LetsEncrypt certificate. Replace both instances of example.com with your domain.

$ sudo certbot certonly --nginx -d example.com,www.example.com

Open the nginx config file again.

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

Replace everything with the following code snippet, save, and exit. Make sure to replace all instances of example.com with your domain.

server {
    listen 80;
    listen [::]:80;
    server_name www.example.com;
    server_name example.com;
    root /var/www/example.com;
    location / {
        return 301 https://example.com$request_uri;
    }
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    root /var/www/example.com;
    index index.php index.html index.htm index.nginx-debian.html;
    add_header X-XSS-Protection "1; mode=block";
    add_header Content-Security-Policy "default-src 'self'; script-src 'self';";
    add_header Referrer-Policy "no-referrer";
    add_header X-Frame-Options "SAMEORIGIN" always;
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    }
    server_name www.example.com;
    server_name example.com;
    access_log /var/log/example.com.log;
    error_log /var/log/example.com.error.log;
}

Link the config file to the nginx sites-enabled directory. Replace example.com with your domain.

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

Restart nginx.

$ sudo systemctl restart nginx.service

6. Test

Open a new file in your web directory.

$ nano /var/www/example.com/testdb.php

Add the following code snippet, save, and exit. Change the username and password below to the ones you set earlier in the MariaDB monitor.

<?php
$mysqli = new mysqli("localhost", "username", "password", "example_db");

if (mysqli_connect_errno()) {
    printf("Connection failed: %s\n", mysqli_connect_error());
    exit();
}

$query = "SELECT column1 FROM table1";

if($result = $mysqli->query($query)) {
    while($row = $result->fetch_row()){
        printf("%s\n", $row[0]);
    }
    $result->close();
}

$mysqli->close();
?>

To verify the server is running with LetsEncrypt, and can access the database correctly, navigate to example.com/testdb.php. Substitute example.com with your domain.

You should see "Database connection established successfully". This verifies the LEMP stack is functioning properly.

For testing LetsEncrypt, you can use ssllabs, which should report an "A" rank for your domain.

About the Nginx Config

The security headers added to the nginx config are

  • X-XSS-Protection "1; mode=block"
    • Prevents cross-site scripting, which stops attackers from injecting code onto the website that other users could see. With mode=block, the browser will not render the page at all if an attack is detected.
  • Content-Security-Policy "default-src 'self'; script-src 'self';"
    • Also prevents cross-site scripting by only allowing scripts to be loaded from the same domain the website is hosted on.
  • Referrer-Policy "no-referrer"
    • No referrer information will be added to the headers. It is mostly for privacy of the user.
  • X-Frame-Options "SAMEORIGIN" always
    • The webpage will only be displayed on the same origin (domain) as itself. It will attempt to prevent browsers from rendering the webpage on a remote website, thereby making phishing attacks, and IP theft a lot harder. Not all browsers are compatible though.

It will also redirect all http requests to https, making it almost impossible for MITM attacks to occur.

Conclusion

You have successfully installed a LEMP stack on your Ubuntu 20.04 LTS VPS. For more information about LEMP, and further guides, see the official documentation:

Want to contribute?

You could earn up to $300 by adding new articles