Installing Nginx, PHP, MariaDB (LEMP Stack) with Opcache, Redis, and Let’s Encrypt on Ubuntu

The LEMP stack is a widely-used combination of software that enables servers to host dynamic websites and web applications. The acronym “LEMP” represents four main components essential for this setup:

  1. Linux: The operating system. Linux is an open-source platform that forms the foundation of the server.
  2. Engine-X (pronounced “Nginx”): A high-performance web server known for its stability, rich features, simple configuration, and low resource consumption.
  3. MySQL or MariaDB: The database management system, typically used to store and manage data for websites and applications. While MySQL is popular, MariaDB serves as a fully open-source alternative.
  4. PHP: A server-side scripting language designed primarily for web development but also used as a general-purpose programming language.

LEMP is often preferred for hosting database-driven and server-side processed websites, akin to the LAMP stack, except that it substitutes Apache with Nginx.

This guide provides step-by-step instructions for installing a LEMP stack on an Ubuntu 20.04-based server. We will also cover the installation of phpMyAdmin, Redis, Opcache, and Let’s Encrypt SSL.

Prerequisites

    • A server running Ubuntu 20.04.
    • A non-root user with sudo privileges.
    • Ensure your system is up to date:
$ sudo apt update
$ sudo apt upgrade
    • Install necessary packages:
$ sudo apt install wget curl nano -y

Note: Some packages might already be pre-installed.

Configure Firewall

The initial step involves configuring the firewall using Ubuntu’s default ufw (Uncomplicated Firewall).

To check its status:

$ sudo ufw status

If it shows:

Status: inactive

Enable SSH port, allowing firewall adjustments without losing connectivity:

$ sudo ufw allow OpenSSH

Also, enable HTTP and HTTPS ports:

$ sudo ufw allow 80
$ sudo ufw allow 443

Activate 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

Recheck the firewall status:

$ sudo ufw status

Expect this 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

To install the updated PHP repository, add Ondrej’s PHP repository:

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

Install PHP 7.4 with additional packages:

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

Verify PHP installation:

$ php --version

This should display something like:

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, a MySQL alternative, is compatible with MySQL commands. Install the latest stable version by adding the official MariaDB 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'

Install MariaDB:

$ sudo apt install mariadb-server -y

Confirm installation:

$ mysql --version

You should see:

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

Enable MariaDB service:

$ sudo systemctl enable mariadb

Secure the installation with the following command:

$ sudo mysql_secure_installation

During the setup, choose the unix_socket plugin for a secure configuration.

Install Redis

Install Redis and its PHP extension:

$ sudo apt install redis php-redis

Configure Redis Server

Open the Redis configuration file:

$ sudo nano /etc/redis/redis.conf

Modify the supervised directive to:

supervised systemd

For remote client connections, change:

bind 0.0.0.0

Optionally, change the port from 6379:

port 3458

Configure it as a cache server:

maxmemory 256mb
maxmemory-policy allkeys-lru

Set a password:

requirepass 

Save and restart Redis:

$ sudo systemctl restart redis

Update the firewall if remote access is necessary:

$ sudo ufw allow 6379/tcp

Install Nginx

Switch to Nginx’s stable repository for the latest version:

$ sudo apt install curl gnupg2 ca-certificates lsb-release
$ 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

Verify installation:

$ nginx -v

This should show:

nginx version: nginx/1.18.0

Start and enable Nginx:

$ sudo systemctl start nginx
$ sudo systemctl enable nginx

Access your server’s IP in a browser, and you should see the default Nginx page:

Nginx Default Page

Configure Nginx

Create directories for server blocks:

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

Set up the directory for your site:

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

Create a configuration file for your site:

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

Insert the following content:

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;
  }
}

Close and save the configuration file. Enable it by linking it to the sites-enabled directory:

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

Edit the Nginx configuration file:

$ sudo nano /etc/nginx/nginx.conf	

Add these lines:

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

Save changes and test the configuration:

$ sudo nginx -t

You should see:

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

Reload Nginx:

$ sudo systemctl reload nginx

Configure PHP-FPM

Edit the PHP-FPM configuration:

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

Adjust the user and group lines from www-data to nginx:

user = nginx
group = nginx

Also update listen.owner and listen.group to nginx:

listen.owner = nginx
listen.group = nginx

Save and restart PHP-FPM:

$ sudo systemctl restart php7.4-fpm

Test PHP setup by creating a test.php file:

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

Insert and save this content:

<?php phpinfo();

Open http://<yourserverip>/test.php in your browser:

PHP Information

Install phpMyAdmin

Install required PHP packages:

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

Download phpMyAdmin files into the /usr/share directory:

$ cd /usr/share
$ 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

Set permissions for phpMyAdmin:

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

Configure phpMyAdmin

Create a configuration file from the sample:

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

Add a secret for cookies and security:

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

Create a symbolic link in Nginx’s root directory:

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

Access phpMyAdmin via http://example.com/phpmyadmin, changing its location enhances security:

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

Access via http://example.com/sm123. Create a MySQL user to use phpMyAdmin:

$ sudo mysql

Create a new user with privileges:

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

Log in using this new user.

Configure Opcache

If missing, install Opcache:

$ sudo apt install php7.4-opcache	

Verify installation:

$ 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

Edit Opcache settings:

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

Add the following settings:

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

Apply changes by restarting your server:

$ sudo systemctl restart php7.4-fpm

Install SSL via Let’s Encrypt

SSL is crucial for website security. Install Certbot:

$ sudo apt install certbot python3-certbot-nginx

Generate SSL certificates:

$ sudo certbot --nginx -d example.com

If running for the first time, follow prompts to set up the certificate. Choose HTTPS configuration:

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):

Your site is now secure via https://example.com.

Verify SSL Auto-Renewal

Ensure certificates renew automatically:

$ sudo certbot renew --dry-run

Without errors, certbot will manage renewal and inform of expiration.

Conclusion

Congratulations! Your LEMP stack is successfully set up. You can now host dynamic websites and web applications smoothly.

FAQ

What is the difference between LEMP and LAMP?
LEMP replaces Apache (in LAMP) with Nginx, offering different configurations and performance benefits.
Why use MariaDB instead of MySQL?
MariaDB is a community-driven fork of MySQL, known for being fully open-source with improved features and performance.
How can I access phpMyAdmin?
Access phpMyAdmin by navigating to http://example.com/phpmyadmin or the hidden directory specified during setup.
What does OPCache do?
OPCache caches precompiled script content, improving PHP performance by reducing server load.
How often does Certbot renew SSL certificates?
Certbot attempts renewal 30 days before certificate expiration, to ensure uninterrupted SSL coverage.