How To Install Linux, Nginx, MySQL, PHP (LEMP stack) on RedHat 8 using Oracle Virtualbox

Introduction

Paul Zhao
Paul Zhao Projects
9 min readJan 25, 2021

--

The LEMP software stack is a group of software that can be used to serve dynamic web pages and web applications. The LEMP stands for a Linux operating system, with an Nginx (pronounced like “Engine-X”) web server. The backend data is stored in the MySQL database and the dynamic processing is handled by PHP.

Diagram of tools
Architecture of a LEMP stack

Prerequisites:

Installation of Redhat 8 using Oracle Virtualbox on Windows 10, please refer to this project

Installation of Redhat 8 using Oracle Virtualbox on Mac, please refer to this project

In redhat 8, you may need to create a non-root user with admin level of privileges using code below

$ sudo useradd -aG wheel <username>

Step 1 — Installing the Nginx Web Server

In order to install Nginx , we need to update our server’s package index

$ sudo yum update -y ### -y means you don't have to type yes to agree to proceed
$ sudo yum install -y nginx

After installing nginx, we need to enable and restart it.

$ systemctl enable nginx 
$ systemctl restart nginx

To check it status

$ systemctl status nginx● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor prese>
Drop-In: /usr/lib/systemd/system/nginx.service.d
└─php-fpm.conf
Active: active (running) since Sun 2021-01-24 17:56:31 EST; 1s ago
Process: 228121 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
Process: 228119 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCES>
Process: 228117 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, stat>
Main PID: 228122 (nginx)
Tasks: 3 (limit: 11259)
Memory: 12.7M
CGroup: /system.slice/nginx.service
├─228122 nginx: master process /usr/sbin/nginx
├─228123 nginx: worker process
└─228124 nginx: worker process
Jan 24 17:56:31 localhost.localdomain systemd[1]: Starting The nginx HTTP and r>
Jan 24 17:56:31 localhost.localdomain nginx[228119]: nginx: [warn] server name >
Jan 24 17:56:31 localhost.localdomain nginx[228119]: nginx: the configuration f>
Jan 24 17:56:31 localhost.localdomain nginx[228119]: nginx: configuration file >
Jan 24 17:56:31 localhost.localdomain nginx[228121]: nginx: [warn] server name >
Jan 24 17:56:31 localhost.localdomain systemd[1]: Started The nginx HTTP and re>

To find your ip address

$ ip a

Notes: The address you apply should be under enp0s3 inet

To double check, you may head to your browser and type in your ip address on port 80, e.g. 192.168.0.18:80

Nginx on port 80

Notes: In case you port 80 and 443 were not open yet, please follow the below instructions to open them

$ sudo firewall-cmd --permanent --zone=public --add-service=http
$ sudo firewall-cmd --permanent --zone=public --add-service=https
$ sudo firewall-cmd --reload

Step 2 — Installing MySQL to Manage Site Data

First, we may install mysql

$ sudo yum install mysql-server -y

When MySQL successfully get installed and started, a temporary root password generated to login into MySQL.

You can find the temporary root password by using the following command:

$ su
Password:
[root@localhost LEMP-Stack]# cat /var/log/mysql/mysqld.log | grep password
2021-01-23T18:43:39.591467Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-i nsecure option.

To secure the installation, MySQL comes with a script that will ask whether we want to modify some insecure defaults. Initiate the script by typing:

[root@localhost mysql]# sudo mysql_secure_installationSecuring the MySQL server deployment.Enter password for user root:
The 'validate_password' component is installed on the server.
The subsequent steps will run with the existing configuration
of the component.
Using existing password for root.
Estimated strength of the password: 100
Change the password for root ? ((Press y|Y for Yes, any other key for No) : y
New password:Re-enter new password:Estimated strength of the password: 100
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : yes
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL 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? (Press y|Y for Yes, any other key for No) : yes
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? (Press y|Y for Yes, any other key for No) : yes
Success.
By default, MySQL 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? (Press y|Y for Yes, any other key for No) : yes
- 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? (Press y|Y for Yes, any other key for No) : yes
Success.
All done!

Notes: In case you may encounter following error

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using passwordS)

Please follow instructions here

Then, we may login as follows

[pzhao@localhost LEMP-Stack]$ sudo mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 33
Server version: 8.0.21 Source distribution
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql>

You may test it as shown below

mysql> SELECT user,authentication_string,plugin,host FROM mysql.user;
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
| user | authentication_string | plugin | host |
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
| mysql.infoschema | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| mysql.session | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| mysql.sys | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| root | *292EAB51A1120213DA244EF2162D5E5FBEBB9327 | mysql_native_password | localhost |
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
4 rows in set (0.02 sec)

For security purpose, we may configure the root account to authenticate with a password, run the following ALTER USER command. Be sure to change password to a strong password of your choosing:

mysql> ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘password’;

Then, run FLUSH PRIVILEGES which tells the server to reload the grant tables and put your new changes into effect:

mysql> FLUSH PRIVILEGES;

Check the authentication methods employed by each of your users again to confirm that root no longer authenticates using the auth_socket plugin:

mysql> SELECT user,authentication_string,plugin,host FROM mysql.user;

+------------------+-------------------------------------------+-----------------------+-----------+
| user | authentication_string | plugin | host |
+------------------+-------------------------------------------+-----------------------+-----------+
| root | *3636DACC8616D997782ADD0839F92C1571D6D78F | mysql_native_password | localhost |
| mysql.session | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost |
| mysql.sys | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost |
| debian-sys-maint | *CC744277A401A7D25BE1CA89AFF17BF607F876FF | mysql_native_password | localhost |
+------------------+-------------------------------------------+-----------------------+-----------+
4 rows in set (0.00 sec)

You can see in this example output that the root MySQL user now authenticates using a password. Once you confirm this on your own server, you can exit the MySQL shell:

mysql> exit

Notes: After configuring your root MySQL user to authenticate with a password, you’ll no longer be able to access MySQL with the sudo mysql command used previously. Instead, you must run the following:

$ mysql -u root -p

After entering the password you just set, you will see the MySQL prompt.

Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 41
Server version: 8.0.21 Source distribution
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql>

Step 3 — Installing PHP and Configuring Nginx to Use the PHP Processor

First, to install PHP 8, we need to enable EPEL or Remi Repository on Redhat 8

$ sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm

or

$ sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm

After the installation is done, you’ll need to run a command to enable the repository containing your preferred version of PHP. To check which PHP 8+ releases are available in the Remi repository, run:

$ yum --disablerepo="*" --enablerepo="remi-safe" list php[7-9][0-9].x86_64

We need to enable this module before installing PHP 8.0. To enable php:remi-8.0, execute:

$ sudo dnf module enable php:remi-8.0 -y

Now we may install PHP 8.0 for Nginx

$ sudo dnf install php php-cli php-common php-fpm

Verify PHP 8.0

$ php -v
PHP 8.0.1 (cli) (built: Jan 5 2021 13:54:54) ( NTS gcc x86_64 )
Copyright (c) The PHP Group
Zend Engine v4.0.1, Copyright (c) Zend Technologies
with Zend OPcache v8.0.1, Copyright (c), by Zend Technologies

Step 4 — Creating a PHP File to Test Configuration

First, open a new file in the /etc/nginx/conf.d directory:

$ sudo vim /etc/nginx/conf.d/default.conf

Copy the following PHP server definition block to your configuration file, and replace the server_name directive so that it points to your server’s domain name or IP address:

/etc/nginx/conf.d/default.conf

server {
listen 80;
server_name server_domain_or_IP;###(e.g. 192.168.0.18/info.php)
root /usr/share/nginx/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

Next, restart Nginx to apply the changes:

$ sudo systemctl restart nginx

Your web server is now fully set up. In the next step, we’ll test the PHP integration to Nginx.

Step 5 — Testing PHP Processing on your Web Server

Before creating our script, we’ll make a change to the default ownership settings on Nginx’s document root, so that our regular sudo user is able to create files in that location.

The following command will change the ownership of the default Nginx document root to a user and group called pzhao, so be sure to replace the highlighted username and group in this command to reflect your system’s username and group.

$ sudo chown -R pzhao.pzhao /usr/share/nginx/html/

We’ll now create a test PHP page to make sure the web server works as expected.

Create a new PHP file called info.php at the /usr/share/nginx/html directory:

$ vim /usr/share/nginx/html/info.php

The following PHP code will display information about the current PHP environment running on the server:

/usr/share/nginx/html/info.php

<?phpphpinfo();?>

Now we can test whether our web server can correctly display content generated by a PHP script. Go to your browser and access your server hostname or IP address, followed by /info.php:

http://server_host_or_IP/info.php

You’ll see a page similar to this:

Php info page

After verifying that Nginx renders the page correctly, it’s best to remove the file you created as it can actually give unauthorized users some hints about your configuration that may help them try to break in. You can always regenerate this file if you need it later.

For now, remove the file by typing:

$ sudo rm /usr/share/nginx/html/info.php

The output on same page

Php info page removed

Conclusion:

In this project, we built a flexible foundation for serving PHP websites and applications to your visitors, using Nginx as web server and the latest PHP release version. You’ve set up Nginx to handle PHP requests through php-fpm, and you also set up a Mysql database to store your website’s data.

We did all this in a local environment. However, we can definitely accomplish LEMP installation using AWS Red Hat or other instances. To learn more about how to provision EC2 instances using AWS CLI with CloudFormation or Terraform, please refer to my post here (CTRL + F and search term “Create redhat instance using CloudFormation” and “terraforming one instance”)

--

--

Paul Zhao
Paul Zhao Projects

Amazon Web Service Certified Solutions Architect Professional & Devops Engineer