Install the LEMP (Linux, NGINX, MariaDB, and PHP) on Ubuntu 22.04

Salam,

Prerequisites

To follow this tutorial, you need a root account on Ubuntu 22.04 OS running on your remote server. If you need a virtual private server (VPS) / a virtual computer with 1 IP public that is online --and can serve– 24/7/365, I recommend HostHatch which has great features like:

  • KVM based with AMD EPYC Milan CPU
  • DDR4 RAM servers
  • NVMe storage
  • Generous monthly bandwidth
  • Multiple data centers around the world, including Amsterdam, Stockholm, United States (Los Angeles, Chicago, New York, Vienna, Oslo, London, Zurich, Warsaw, Madrid, Milan, Hongkong, Singapore, Tokyo, and Sydney.
  • Starts from $4/Month

Step 1: Update Software Packages

Before we install the LEMP (Linux, EngineX (NGINX), MariaDB, and PHP) stack, we need to update the Ubuntu system package with the following command:

sudo apt update

Step 2: Install Nginx Web Server (Stable Latest)

NGINX is a high-performance web server and is very popular these days. It also can be used as a reverse proxy and caching server. Follow these steps to install Nginx Web server based on the nginx packages repository.

Install the prerequisites:

sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring

Import an official nginx signing key so apt could verify the packages authenticity. Fetch the key:

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

To set up the apt repository for stable nginx packages, run the following command:

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

Set up repository pinning to prefer our packages over distribution-provided ones:

echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

To install nginx, run the following commands:

sudo apt update
sudo apt install nginx

To check whether nginx is successfully installed or not, run the following command:

nginx -v

Output:

root@ariq01:~# nginx -v
nginx version: nginx/1.22.1

Start and Enable NGINX service to ensure it's automatically starting at boot time.

systemctl start nginx && systemctl enable nginx

Now check out its status:

sudo systemctl status nginx

Output:

root@ariq01:~# systemctl status nginx
● nginx.service - nginx - high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2023-02-08 04:34:44 UTC; 3h 9min ago
       Docs: https://nginx.org/en/docs/
   Main PID: 622 (nginx)
      Tasks: 2 (limit: 1116)
     Memory: 3.3M
        CPU: 8ms
     CGroup: /system.slice/nginx.service
             ├─622 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
             └─623 "nginx: worker process
Press ctrl+c to close this systemctl status command

Step 3: Install MariaDB Database Server (Latest / MariaDB 10.6.11)

MariaDB is a community-developed, commercially supported fork of the MySQL relational database management system (RDBMS), intended to remain free and open-source software under the GNU General Public License. Enter the following command to install MariaDB on Ubuntu 20.04:

sudo apt install mariadb-server mariadb-client

Now check out its status:

systemctl status mariadb

Output:

● mariadb.service - MariaDB 10.6.11 database server
     Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2023-02-08 08:24:55 UTC; 2h 0min ago
       Docs: man:mariadbd(8)
             https://mariadb.com/kb/en/library/systemd/
    Process: 2752 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysqld (code=exited, status=0/SUCCESS)
    Process: 2753 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)
    Process: 2755 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] && VAR= ||   VAR=`cd /usr/bin/..; /usr/bin/galera_recovery`; [ $? -eq 0 ]   && systemctl set-environment _WSREP_>
    Process: 2795 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)
    Process: 2797 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)
   Main PID: 2784 (mariadbd)
     Status: "Taking your SQL requests now..."
      Tasks: 7 (limit: 1116)
     Memory: 65.2M
        CPU: 1.572s
     CGroup: /system.slice/mariadb.service
             └─2784 /usr/sbin/mariadbd
Press ctrl+c to close this systemctl status command

To enable MariaDB to automatically start at boot time, run this following command:

sudo systemctl enable mariadb

Now run the post-installation security script.

sudo mysql_secure_installation

It'll ask you to enter MariaDB root password, press Enter key as the root password isn’t set yet. Then enter y to set the root password for MariaDB server.

Output:

root@ariq01:~# sudo mysql_secure_installation

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

And then you can press Enter to answer all remaining questions, which using unix_socket authentication or not? remove an anonymous user, disable remote root login, and remove the test database.

Note :

If you decided to use unix_socket_authentication, after the mysql_secure_installation completed, you can login mysql root account without password (localhost only).
Switch to unix_socket authentication [Y/n]

Press Y to use unix_socket_authentication.

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] 

You can safely answer 'n' because it's already protected by unix_socket_authentication.

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] y
 ... 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] y
 ... 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] y
 - 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] y
 ... 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!

Check MariaDB server version information with the following command:

mariadb --version
mariadb  Ver 15.1 Distrib 10.6.11-MariaDB, for debian-linux-gnu (x86_64) using  EditLine wrapper

Step 4: Install PHP (PHP 7.4)

We cannot install the PHP7.4 packages using the default system repository of Ubuntu 22.04 because the default version of PHP present to install in this Ubuntu version is PHP 8.1. Hence, to get the older version, add the PPA repository called Ondrej.

sudo apt install software-properties-common

And add an apt repository Ondrej

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

Output:

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

PPA publishes dbgsym, you may need to include 'main/debug' component
Repository: 'deb https://ppa.launchpadcontent.net/ondrej/php/ubuntu/ jammy main'
Description:
Co-installable PHP versions: PHP 5.6, PHP 7.x, PHP 8.x and most requested extensions are included. Only Supported Versions of PHP (http://php.net/supported-versions.php) for Supported Ubuntu Releases (https://wiki.ubuntu.com/Releases) are provided. Don't ask for end-of-life PHP versions or Ubuntu release, they won't be provided.

Debian oldstable and stable packages are provided as well: https://deb.sury.org/#debian-dpa

You can get more information about the packages at https://deb.sury.org

IMPORTANT: The <foo>-backports is now required on older Ubuntu releases.

BUGS&FEATURES: This PPA now has a issue tracker:
https://deb.sury.org/#bug-reporting

CAVEATS:
1. If you are using php-gearman, you need to add ppa:ondrej/pkg-gearman
2. If you are using apache2, you are advised to add ppa:ondrej/apache2
3. If you are using nginx, you are advised to add ppa:ondrej/nginx-mainline
   or ppa:ondrej/nginx

PLEASE READ: If you like my work and want to give me a little motivation, please consider donating regularly: https://donate.sury.org/

WARNING: add-apt-repository is broken with non-UTF-8 locales, see
https://github.com/oerdnj/deb.sury.org/issues/56 for workaround:

# LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
More info: https://launchpad.net/~ondrej/+archive/ubuntu/php
Adding repository.
Adding deb entry to /etc/apt/sources.list.d/ondrej-ubuntu-php-jammy.list
Adding disabled deb-src entry to /etc/apt/sources.list.d/ondrej-ubuntu-php-jammy.list
Adding key to /etc/apt/trusted.gpg.d/ondrej-ubuntu-php.gpg with fingerprint 14AA40EC0831756756D7F66C4F4EA0AAE5267A6C
Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease
Get:2 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]
Get:3 https://pkgs.tailscale.com/stable/ubuntu jammy InRelease
Get:4 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB]
Hit:5 http://nginx.org/packages/ubuntu jammy InRelease
Get:6 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [107 kB]
Get:7 https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy InRelease [23.9 kB]
Get:8 https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy/main amd64 Packages [110 kB]
Get:9 https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy/main Translation-en [34.2 kB]
Fetched 510 kB in 3s (189 kB/s)
Reading package lists... Done

Now, we can install PHP7.4 on our Ubuntu 22.04 Linux, run the following command:

sudo apt install php7.4-{fpm,mysql,common,cli,json,opcache,readline,mbstring,xml,gd,curl,zip}

Installing these PHP extensions ensures that your CMS runs correctly and smoothly. Now start php7.4-fpm.

sudo systemctl start php7.4-fpm

Enable php7.4 service at boot time with the following command:

sudo systemctl enable php7.4-fpm

Check status:

systemctl status php7.4-fpm

Output:

root@ariq01:~# systemctl status php7.4-fpm
● php7.4-fpm.service - The PHP 7.4 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php7.4-fpm.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2023-02-08 11:03:35 UTC; 1min 59s ago
       Docs: man:php-fpm7.4(8)
    Process: 14311 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/7.4/fpm/pool.d/www.conf 74 (c>
   Main PID: 14308 (php-fpm7.4)
     Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
      Tasks: 3 (limit: 1116)
     Memory: 11.7M
        CPU: 73ms
     CGroup: /system.slice/php7.4-fpm.service
             ├─14308 "php-fpm: master process (/etc/php/7.4/fpm/php-fpm.conf)
             ├─14309 "php-fpm: pool www
             └─14310 "php-fpm: pool www
Press ctrl+c to close this systemctl status command

Looks like the php7.4-fpm service already started, let's forward to the next step:

Step 5: Configuring NGINX: Create an NGINX Server Block

In order to NGINX serve a web server, we need to create an NGINX server block first on /etc/nginx/conf.d directory.

Backup the default config file /etc/nginx/conf.d/default.conf

mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
Don't worry a .bak file is not loaded on nginx.conf due the line include /etc/nginx/conf.d/*.conf; (Only *.conf loaded in /etc/nginx/conf.d directory)

Create an NGINX server block ( a .conf .file) by moving to the directory first by using your favorite text editor:

nano /etc/nginx/conf.d/mydomain.conf

Paste the following text into the file:

server {
  listen 80;
  listen [::]:80;
  server_name _;
  root /var/www/html/;
  index index.php index.html;

  location / {
    try_files $uri $uri/ /index.php;
  }

	location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
	add_header X-Cache $upstream_cache_status;
    }


 # A long browser cache lifetime can speed up repeat visits to your page
  location ~* \.(jpg|jpeg|gif|png|webp|svg|woff|woff2|ttf|css|js|ico|xml)$ {
       access_log        off;
       log_not_found     off;
       expires           360d;
  }

  # disable access to hidden files
  location ~ /\.ht {
      access_log off;
      log_not_found off;
      deny all;
  }
}
NGINX server block

Save and close the file by pressing CTRL + X and Y for Yes.

Note:
- The first and second line will make Nginx listen on IPv4 port 80 and IPv6 port 80. If you don't have IPV6 connectivity, you can comment it by adding # before listen [::]:80;
- Replace _ on server_name section to your domain, for example server_name mydomain.com www.mydomain.com
- If your VPS have 1 IP public address, you can replace _ on server_name section to your IP public address.

Don't forget to always test nginx configuration with

nginx -t

If the syntax is ok, then reload NGINX by

systemctl reload nginx

Step 6: Configuring NGINX: Check nginx.conf configuration

After an NGINX configuration has been successfully created, we need to check nginx.conf first by opening the file

nano /etc/nginx/nginx.conf

By default installation, the nginx service run as nginx user, we need to change the user to www-data in order to php and nginx services running smoothly.


user  www-data;
worker_processes  auto;

Save it by closing the nano by pressing CTRL + X, then press Y and enter.

Remember: Always test nginx config with nginx -t and reload nginx with systemctl reload nginx after making modifications!

Step 7: Test PHP

To test PHP-FPM with NGINX, we need to create info.php in the web root directory. Since the webroot path is /var/www/html/ on Step 5 section, then create info.php inside of it.

nano /var/www/html/info.php

Paste the following PHP code into the file.

<?php phpinfo(); ?>

Save and close the file.

Now in the browser address bar, enter server-ip-address/info.php or domain.com/info.php

You should see your server’s PHP information. This means PHP scripts can run properly with Nginx web server.

Congrats! You have successfully installed Nginx, MariaDB, and PHP7.4 on Ubuntu 20.04. For your server’s security, you should delete info.php file now to prevent hackers from seeing it.

This tutorial was modified from:

If you want to give Ondrej Sury (PHP repository on Ubuntu/Debian maintainer) a little motivation, consider donating to him regularly on his GitHub (https://github.com/oerdnj)

Thanks!

I hope this tutorial is useful for you guys.
See ya!