There is a new version of this tutorial available for Ubuntu 24.04 (Noble Numbat).

How to Install Nginx with PHP and MariaDB (LEMP Stack) with Opcache, Redis and Let's Encrypt on Ubuntu

The acronym "LEMP" stands for a group of software that is typically installed together to enable a server to host dynamic websites and web applications. This term is actually an acronym representing four key components:

  1. Linux: The operating system. Linux is a popular open-source operating system that serves as the foundation for the server.
  2. Engine-X (pronounced as "nginx"): The web server. Nginx is a high-performance web server that is known for its stability, rich feature set, simple configuration, and low resource consumption.
  3. MySQL or MariaDB: The database system. MySQL is a widely-used relational database management system that stores and manages the data for the website or application. Note that MySQL is sometimes replaced with MariaDB, an enhanced, fully open-source, community-developed fork of MySQL.
  4. PHP: The programming language. PHP is a server-side scripting language designed for web development, but also used as a general-purpose programming language.

The LEMP stack is a popular choice for hosting websites that require a database and server-side processing, such as WordPress websites, e-commerce platforms, and other dynamic web applications. It is comparable to the LAMP stack, where Apache (represented by the "A" in LAMP) is used instead of Nginx.

In this guide, you will install a LEMP stack on a Ubuntu 20.04-based server. We will also install phpMyAdmin, Redis, Opcache, and Let's Encrypt SSL.

Prerequisites

  • A server running Ubuntu 20.04.

  • A non-root sudo user.

  • Make sure everything is updated.

    $ sudo apt update
    $ sudo apt upgrade
    
  • Few packages that your system needs.

    $ sudo apt install wget curl nano -y
    

    Some of these packages may already be installed on your system.

Configure Firewall

The first step is to configure the firewall. Ubuntu comes with ufw (Uncomplicated Firewall) by default.

Check if the firewall is running.

$ sudo ufw status

You should get the following output.

Status: inactive

Allow SSH port so that the firewall doesn't break the current connection on enabling it.

$ sudo ufw allow OpenSSH

Allow HTTP and HTTPS ports as well.

$ sudo ufw allow 80
$ sudo ufw allow 443

Enable the Firewall

$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

Check the status of the firewall again.

$ sudo ufw status

You should see a similar output.

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80                         ALLOW       Anywhere
443                        ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80 (v6)                    ALLOW       Anywhere (v6)
443 (v6)                   ALLOW       Anywhere (v6)

Install PHP

Ubuntu 20.04 by default ships with PHP 7.4 but to have an updated PHP repository, we will add the Ondrej's PHP repository.

Install Ondrej's PHP repository.

$ sudo add-apt-repository ppa:ondrej/php

Install PHP 7.4 along with some additional packages.

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

Check if PHP is working correctly.

$ php --version

You should see a similar output.

PHP 7.4.5 (cli) (built: Apr 28 2020 14:49:23) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.5, Copyright (c), by Zend Technologies

Install MariaDB

MariaDB is a drop-in replacement for MySQL which means commands to run and operate MariaDB are the same as those for MySQL.

Ubuntu 20.04 by default ships with MariaDB 10.3 which is a bit outdated. To get the latest stable version of Mariadb, we will install its official repository.

Add Mariadb's Official repository.

$ sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
$ sudo add-apt-repository 'deb [arch=amd64] http://mirror.lstn.net/mariadb/repo/10.4/ubuntu focal main'

To install MariaDB issue the following command.

$ sudo apt install mariadb-server -y

Check if MariaDB installed correctly.

$ mysql --version

You should see the following output.

mysql  Ver 15.1 Distrib 10.4.13-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2

Enable the MariaDB service.

$ sudo systemctl enable mariadb

Run the following command to perform default configuration such as giving a root password, removing anonymous users, disallowing root login remotely and dropping test tables.

$ sudo mysql_secure_installation

With MariaDB 10.4, you will now be asked between using the root password or unix_socket plugin. The unix_socket plugin allows you to log in to MariaDB with your Linux user credentials. It is considered more secure though you will need a traditional username/password to use 3rd party apps like phpMyAdmin. We will stick to using unix_socket plugin for this tutorial. You can still use phpMyAdmin via any user you specific user you create for your databases.

Pressing Enter chooses the default option (the one that is capitalised, Y in this case).

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user. If you've just installed MariaDB, and
haven't set the root password yet, you should just press enter here.

Enter current password for root (enter for none): [PRESS ENTER]
OK, successfully used password, moving on...

Setting the root password or using the unix_socket ensures that nobody
can log into the MariaDB root user without the proper authorisation.

You already have your root account protected, so you can safely answer 'n'.

Switch to unix_socket authentication [Y/n] [PRESS ENTER]
Enabled successfully!
Reloading privilege tables..
 ... Success!

You already have your root account protected, so you can safely answer 'n'.

Change the root password? [Y/n] [ANSWER n]
... skipping.

By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] [PRESS ENTER]
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] [PRESS ENTER]
 ... Success!

By default, MariaDB comes with a database named 'test' that anyone can
access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] [PRESS ENTER]
 \- Dropping test database...
 ... Success!
 \- Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] [PRESS ENTER]
 ... Success!

Cleaning up...

All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!

That's it. Next time you want to login to MySQL, use the following command

$ sudo mysql

Enter your root password when prompted.

Install Redis

Use the following command to install Redis and the corresponding PHP Redis extension.

$ sudo apt install redis php-redis

Configure Redis Server

Let us perform some basic configurations on the Redis server.

Open the file /etc/redis/redis.conf with Nano editor.

$ sudo nano /etc/redis/redis.conf

Inside the file, locate the supervised directive. This directive allows you to declare an init system to manage Redis as a service. It is set to no by default. Since we are using Ubuntu which uses the systemd init system, change its value from no to systemd as follows.

supervised systemd

If you want remote clients to connect to your Redis instance then find the line bind 127.0.0.1 and change it to the following.

bind 0.0.0.0

You can also change the default port on which Redis listens to from 6379 to a value of your choice.

port 3458

To configure Redis as a cache server, set the following values as given.

maxmemory 256mb
maxmemory-policy allkeys-lru

This tells Redis to remove any key using the LRU algorithm when the maximum memory of 256MB is reached. You can set the memory value as per your requirement and the server you are using.

You can set a password so that any client which needs Redis will be required to authenticate first. To do that set a password using the following directive.

requirepass  <AuthPassword>

You can find more directives to change in the configuration file. Once you are finished, press Ctrl + X and enter Y when prompted to save the file.

Restart the Redis server to apply the changes.

$ sudo systemctl restart redis

We will also need to add the rule in our Firewall if you want remote clients to connect to it. Otherwise, you can skip this step.

$ sudo ufw allow 6379/tcp

You will need to change the value in the above command to match whatever port you chose in the configuration file above.

Install Nginx

Ubuntu 20.04 by default ships with Nginx's latest Mainline version. (1.17.10). We will, however, switch over to using Nginx's official stable repository.

Install some prerequisites first.

$ sudo apt install curl gnupg2 ca-certificates lsb-release

Some of these are already installed on your system.

Add Nginx's Repository.

$ echo "deb [arch=amd64] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" | sudo tee /etc/apt/sources.list.d/nginx.list
$ curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -

Install Nginx.

$ sudo apt update
$ sudo apt install nginx -y

Check if it is working correctly.

$ nginx -v

You should see the following output depending upon the version of Nginx you chose to install.

nginx version: nginx/1.18.0

Start and enable Nginx.

$ sudo systemctl start nginx
$ sudo systemctl enable nginx

Open your server's IP address in a browser to see the following page. It means Nginx is working properly.

Nginx Default Page

Configure Nginx

Set up directories where the server blocks will live.

$ sudo mkdir /etc/nginx/sites-available
$ sudo mkdir /etc/nginx/sites-enabled

Create the directory where your site will live.

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

Using the -p directive creates parent directories which didn't exist before.

Run the following command to add a configuration file for your site.

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

Paste the following code in the editor.

server {
  listen          *:80;
  server_name     example.com;
  root            /var/www/example.com/html;
  index           index.php index.html;

  location / {
    try_files   $uri $uri/ =404;
  }
    
  access_log /var/log/nginx/example.com.access.log;
  error_log /var/log/nginx/example.com.error.log;

  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass  unix:/run/php/php7.4-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    include  fastcgi_params;
  }
}

Press Ctrl + X to close the editor and press Y when prompted to save the file.

This file assumes that we will be hosting example.com in the directory /var/www/html. If you are not going to use any domain and configuring your server to be accessible just via the IP address/localhost, you will need to remove corresponding server block settings from the nginx.conf file otherwise it will mess with the server block you will create.

Activate this configuration file by linking it to the sites-enabled directory.

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

Open the /etc/nginx/nginx.conf file for editing.

$ sudo nano /etc/nginx/nginx.conf	

Paste the following lines after the line include /etc/nginx/conf.d/*.conf

include /etc/nginx/sites-enabled/*.conf;
server_names_hash_bucket_size 64;
types_hash_max_size 4096;

Press Ctrl + X to close the editor and press Y when prompted to save the file. Test the Nginx configuration.

$ sudo nginx -t

You should see the following output indicating your configuration is correct.

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

Reload the Nginx service.

$ sudo systemctl reload nginx

Configure PHP-FPM

Open the file /etc/php-fpm.d/www.conf.

$ sudo nano /etc/php/7.4/fpm/pool.d/www.conf

We need to set the Unix user/group of PHP processes to nginx. Find the user=www-data and group=www-data lines in the file and change them to nginx.

...
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
; RPM: apache user chosen to provide access to the same directories as httpd
user = nginx
; RPM: Keep a group allowed to write in log dir.
group = nginx
...

Also, find the lines listen.owner=www-data and listen.group=www-data in the file and change them to nginx.

listen.owner = nginx
listen.group = nginx

Save the file by pressing Ctrl + X and entering Y when prompted.

Restart the PHP-fpm process.

$ sudo systemctl restart php7.4-fpm

To test your PHP setup, create a file test.php in the html folder.

$ sudo nano /var/www/example.com/html/test.php

Add the following content to it and save the file by pressing Ctrl + X and entering Y when prompted.

<?php phpinfo();

Launch http://<yourserverip>/test.php in your web browser and you should see the following.

PHP Information

Install phpMyAdmin

We will be installing phpMyAdmin manually for our tutorial.

First, we need to install some additional PHP packages for phpMyAdmin to work.

$ sudo apt install php-mbstring php-zip php-gd php-json php-curl php-bz2 php-xml

Switch to /usr/share directory to download and install phpMyAdmin to.

$ cd /usr/share

Download phpMyAdmin files. (Check the latest version from its site) We will only install the English language version.

$ sudo wget https://files.phpmyadmin.net/phpMyAdmin/5.0.2/phpMyAdmin-5.0.2-english.tar.gz
$ tar xvzf phpMyAdmin-5.0.2-english.tar.gz
$ sudo mv phpMyAdmin-5.0.2-english /usr/share/phpmyadmin
$ sudo rm phpMyAdmin*.tar.gz

Give proper permissions to the phpMyAdmin folder.

$ sudo chown -R nginx:nginx phpmyadmin 
$ sudo chmod -R 744 phpmyadmin 

Configure phpMyAdmin

Create the configuration file from the sample file provided with the package.

$ sudo cp /usr/share/phpmyadmin/config.sample.inc.php /usr/share/phpmyadmin/config.inc.php

We need to add a secret value in the configuration file for cookie and security purposes. Use the following commands to generate and add the code.

$ randomBlowfishSecret=$(openssl rand -base64 32)
$ sed -i "s|cfg\['blowfish_secret'\] = ''|cfg['blowfish_secret'] = '$randomBlowfishSecret'|" /usr/share/phpmyadmin/config.inc.php

For Nginx webserver to find and serve phpMyAdmin files correctly, we will need to create a symbolic link from its actual location to Nginx's root document directory.

To do this, run the following command.

$ sudo ln -s /usr/share/phpmyadmin /var/www/example.com/html/phpmyadmin

Your phpMyAdmin installation is operational. To access it, just open http://example.com/phpmyadmin.

By default, this default location should be changed as it is the most common location any hacker can locate your phpMyAdmin install. To do this, run the following command.

$ sudo mv /var/www/example.com/html/phpmyadmin /var/www/example.com/html/sm123

Basically, we moved our phpMyAdmin location to sm123 folder. To access it, you will now need to open http://example.com/sm123 in your browser.

Since we are using unix_authentication with MySQL here, there is no root user to log into via phpMyAdmin. You will have to create a MySQL user first and give it privileges to databases to be able to use phpMyAdmin.

To do that, log in to MySQL shell.

$ sudo mysql

Now, paste the following commands to create a new user and grant it all the database privileges.

CREATE USER 'user'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'user'@'localhost';
FLUSH PRIVILEGES;
EXIT

You can now log in using this user at http://example.com/phpmyadmin.

Configure Opcache

If you have been following this tutorial, Opcache should already be installed with PHP. In case it isn't, you can simply install Opcache using the following command.

$ sudo apt install php7.4-opcache	

Verify that it has been installed.

$ php -v
PHP 7.4.5 (cli) (built: Apr 28 2020 14:49:23) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.5, Copyright (c), by Zend Technologies

To change Opcache settings, open the file /etc/php/7.4/fpm/conf.d/10-opcache.ini.

$ sudo nano /etc/php/7.4/fpm/conf.d/10-opcache.ini

Paste the following code at the end of the file. The following settings should get you started with using Opcache and are generally recommended as good performance.

opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60

Save the file by pressing Ctrl + X and entering Y when prompted.

Restart your server to apply the settings.

$ sudo systemctl restart php7.4-fpm

Install SSL via Let's Encrypt

SSL has become an essential part of any website. Here we will be installing SSL using Let's Encrypt service.

For that first, install Certbot tool.

$ sudo apt install certbot python3-certbot-nginx

Generate the certificates.

$ sudo certbot --nginx -d example.com

If this is your first time running certbot on your system, you will be asked for an email address and to agree to the terms of service. You will also be asked whether you agree to share data with EFF foundation which you can say no to. After doing so, certbot will communicate with Let's Encrypt servers and run a challenge to verify your domains.

If that is successful, you will be asked how to handle HTTPS redirects.

Please choose whether HTTPS access is required or optional.
-------------------------------------------------------------------------------
1: Easy - Allow both HTTP and HTTPS access to these sites
2: Secure - Make all requests redirect to secure HTTPS access
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Select your choice and press enter. Your certificates will then be created and your Nginx configuration files are updated with SSL settings.

Your certificates are ready and you can open your site now by going to https://example.com

Verify SSL Auto-Renewal

This is the last step before we end this tutorial.

Check the renewal process by doing a dry run of the renewal process.

$ sudo certbot renew --dry-run

If you get no errors, it means you are set. Certbot will automatically renew your certificates for you. You will be sent an email warning about you the expiration of the certificate.

Conclusion

That's all to this tutorial. Your LEMP Setup is complete and you can start making and hosting your websites and applications.

Share this page:

1 Comment(s)