This tutorial provides the steps required to install LEMP Stack using Ubuntu 20.04 LTS server as listed below. The steps should be the same or similar on other versions of Ubuntu and Linux systems.
Linux - Ubuntu 20.04
Nginx - Nginx 1.17
MySQL - MySQL 8
PHP - PHP 7.4
phpMyAdmin
Prerequisites
This tutorial assumes that you have already completed the installation of the Ubuntu 20.04 LTS server on your preferred service provider. It also assumes that the standard web server ports 80 and 443 are publicly accessible. The popular service providers include AWS, Linode, Digital Ocean, UpCloud, Vultr, etc. You can also follow the below-listed tutorials to spin up Ubuntu 20.04 LTS.
- Install Ubuntu 20.04 LTS Desktop
- Install Ubuntu 20.04 LTS On Windows Using VMware
- Spin Up Ubuntu 20.04 LTS Server On Amazon EC2
Install Nginx On Ubuntu 20.04 LTS
This section provides the steps to install Nginx 1.17 on Ubuntu 20.04 LTS. The commands required to install Nginx are shown below.
# Refresh the indexes sudo apt update
# Install nginx sudo apt install nginx
# Check and enable Nginx - Auto start sudo systemctl is-enabled nginx sudo systemctl enable nginx
# Check Nginx Version nginx -v
# Output nginx version: nginx/1.17.10 (Ubuntu)
# Check Nginx Status sudo systemctl status nginx
# Output ● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2020-06-18 05:55:53 UTC; 24s ago Docs: man:nginx(8) Main PID: 2640 (nginx) Tasks: 3 (limit: 1119) Memory: 5.8M CGroup: /system.slice/nginx.service ├─2640 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; ├─2641 nginx: worker process └─2642 nginx: worker process ---- ----
The above-mentioned commands installed Nginx 1.17.10 while writing this tutorial. The important configurations and directories of Nginx installed from the Ubuntu repositories are listed below.
- Configurations Directory - /etc/nginx/conf.d - Additional server configurations. It's empty by default.
- Modules Available Directory - /etc/nginx/modules-available - The directory to store the modules configurations.
- Modules Enabled Directory - /etc/nginx/modules-available - The directory the symlinks of the modules configurations. The symlinks of image filter, xslt filter, mail, and stream modules are available by default.
- Sites Available Directory - /etc/nginx/sites-available - The directory to store the server blocks. The default server block is available within this directory.
- Sites Enabled Directory - /etc/nginx/sites-enabled - The directory having the symlinks of the server blocks available at /etc/nginx/sites-available. We can enable or disabled the server blocks available at /etc/nginx/sites-available by creating or deleting the symlinks. The symlink of the default server block is available within this directory.
- Main Configuration - /etc/nginx/nginx.conf - The main configuration of Nginx. View it on GitHub.
- Default Server Block - /etc/nginx/sites-available/default - The default server block. View it on GitHub.
The default www directory of Nginx is /var/www/html. It serves the static files from this directory using the default server block. Now open the Nginx's Home Page in your favorite browser by using the Server's public IP address or the DNS name assigned by the service provider. It should show the Home Page similar to Fig 1. It can also be accessed using http://localhost in case it's installed on a local desktop or server system.
You can follow How To Install And Configure Nginx on Ubuntu 20.04 LTS for more details and Configure Virtual Host Or Server Block On Nginx to configure the websites for production usage. You can also follow How To Install Let's Encrypt For Nginx On Ubuntu and Redirect HTTP to HTTPS on Nginx to secure your websites using Let's Encrypt.
Install MySQL 8 On Ubuntu 20.04 LTS
This section provides the steps required to install MySQL 8 on Ubuntu 20.04 LTS. Now execute the below-mentioned command to install MySQL 8.
# Install MySQL Server 8 sudo apt install mysql-server
# MySQL Status sudo systemctl status mysql
# Output ● mysql.service - MySQL Community Server Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2020-06-18 05:57:29 UTC; 11s ago Main PID: 3468 (mysqld) Status: "Server is operational" Tasks: 39 (limit: 1119) Memory: 317.3M CGroup: /system.slice/mysql.service └─3468 /usr/sbin/mysqld -----
It installs MySQL core, server, and client packages. Also, the MySQL server version 8.0.20 was installed while writing this tutorial. Configure and secure the installation using the command mysql_secure_installation as shown below. It will ask to set the root password and a few security questions.
Step 1 - Secure Installation - Execute the command to start the MySQL configuration.
# Secure MySQL sudo mysql_secure_installation
Step 2 - Password Validator - It asks to confirm the usage of the password validator to validate the password. It will also show the password strength while providing the password.
# Password Validator Component Press y|Y for Yes, any other key for No: y
If we select No, it won't check the password strength of the MySQL root and other users while adding them. We should use a strong password for the MySQL users, hence it's recommended to use the Password Validator Component.
Step 3 - Password Validation Level - The secure installation command asks the password validation level and provides options to choose among Low(0), Medium(1), and Strong(2). It's recommended to use at least Medium Level to have a strong password of all the MySQL users. The validation rules of all the levels are as listed below.
Low - It expects a password having at least 8 characters without any restriction on the characters.
Medium - The Medium Level expects a password that has at least 8 characters and allows numeric, uppercase, lowercase, and special characters.
Strong - The Strong Level expects a password that has at least 8 characters and allows numeric, uppercase, lowercase, and special characters. It also allows the dictionary file.
# Password Validation Level Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 1
Step 4 - Root Password - The default authentication plugin used by MySQL for the root user is auth_socket.
# Password Prompt New password:<password> Re-enter new password:<repeat password>
In case you have selected to use Password Validator, it will also show the password strength of the root password and confirm to use the given password as shown below.
# Confirm Password Estimated strength of the password: 80 Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y
If we opt for No, it will again prompt for the password.
Step 5 - Remove Anonymous Users - After providing the password, the secure installation process asks to remove the anonymous users. MySQL adds an anonymous user while installing it for testing purposes and allows anyone to log in without a password. It's recommended to remove the anonymous user.
Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Step 6 - Disallow Remote Login - The secure installation process also asks to confirm whether remote login is allowed for the root user. We should choose option y to restrict the root user to the localhost. We can always add additional users to allow remote login when required.
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Step 7 - Remove Test Database - MySQL creates the test database while installing it. You may keep the test database for analysis purposes and later drop it.
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
Step 8 - Reload Privilege Tables - At last, the secure installation asks to reload the privileges tables to immediately apply the changes.
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Now we will change the password plugin of the root user to caching_sha2_password to allow the other applications including phpMyAdmin to login to MySQL server using the root user. You may skip this in case the root user is not required by other applications including phpMyAdmin.
# Login to MySQL sudo mysql
# Check password scheme of root user mysql> SELECT user,authentication_string,plugin,host FROM mysql.user;
# Note the password plugin of root user +------------------+------------------------------------------------------------------------+-----------------------+-----------+ | user | authentication_string | plugin | host | +------------------+------------------------------------------------------------------------+-----------------------+-----------+ | debian-sys-maint | $A$005$[DA NP9|K1zAmHe`LVwrhII7zBo5b5xUoPnvOLuCa9CSJVqCn7W1rzOCCyZD | caching_sha2_password | localhost | | 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 | | auth_socket | localhost | +------------------+------------------------------------------------------------------------+-----------------------+-----------+
# Change the password plugin - caching_sha2_password mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY '<pw>';
# Apply changes mysql> flush privileges;
# Check password scheme of root user mysql> SELECT user,authentication_string,plugin,host FROM mysql.user;
# Note the password plugin of root user +------------------+-------------------------------------------+-----------------------+-----------+ | user | authentication_string | plugin | host | +------------------+-------------------------------------------+-----------------------+-----------+ | root | $A$005$ZtYD-ppbn>iO�"MHhl/0TXh9Qo3xYdWK3ThKPmDB6r.QhVlZY1dcT1LWH0A | caching_sha2_password | localhost | +------------------+-------------------------------------------+-----------------------+-----------+
# Quit Database mysql> exit
Apart from changing the root user password plugin, we can also add additional users and databases as shown below.
# Login to MySQL sudo mysql # OR mysql -u root -p
# Create Users mysql> CREATE USER 'projecta'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'password'; mysql> CREATE USER 'projectb'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'password';
# Create Database - with default character set mysql> CREATE DATABASE projecta;
# Create Database - with utf8mb4 character set - preferred mysql> CREATE DATABASE projectb CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
# Grant privileges to the users mysql> GRANT ALL PRIVILEGES ON projecta.* TO 'projecta'@'localhost'; mysql> GRANT ALL PRIVILEGES ON projectb.* TO 'projectb'@'localhost';
# Apply Changes mysql> FLUSH PRIVILEGES;
# Exit MySQL mysql> exit;
You can also follow How To Install MySQL 8 on Ubuntu 20.04 LTS to learn more about installing MySQL 8.
Install PHP 7.4 On Ubuntu 20.04 LTS
This section provides the steps to install PHP 7.4 on Ubuntu 20.04 LTS.
Nginx needs PHP FPM to process the PHP requests. Use the below-mentioned command to install PHP 7 only if required. It also installs the Apache Web Server which is not required as part of the LEMP stack.
# Install PHP - Install only if required - It also install Apache Web Server sudo apt install php7.4
# Verify PHP php --version
# Output PHP 7.4.3 (cli) (built: May 26 2020 12:24:22) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies
We can install PHP 7 for LEMP stack on Ubuntu 20.04 LTS as shown below.
# Install PHP FPM - Required by Nginx sudo apt install php7.4-fpm
# Check whether enabled on system boot sudo systemctl is-enabled php7.4-fpm
# Output enable
# Install MySQL Extension sudo apt install php7.4-mysql
# Install CGI and CLI if not installed by default sudo apt install php7.4-common php7.4-cgi php7.4-cli
# Install CURL and JSON extensions sudo apt install php7.4-curl php7.4-json
# Install PHP GD and Imagick sudo apt install php7.4-gd php-imagick
# Multibyte String, Internationalization and Spell Check sudo apt install php7.4-mbstring php7.4-intl php7.4-pspell
# Multibyte String, Internationalization and Spell Check - Required for Wordpress Installation sudo apt install php-mbstring php-intl php-pspell
# Emails sudo apt install php7.4-imap
# Tidy and XML RPC sudo apt install php7.4-tidy php7.4-xmlrpc
# Excel sudo apt-get install php7.4-xsl
# Install OPcache extension sudo apt-get install php7.4-opcache
# Install Zip sudo apt-get install php7.4-zip
Also, make sure that PHP-FPM is active and running. It's required by Nginx to process the PHP requests.
PHP FPM Status sudo systemctl status php7.4-fpm
# Output ● 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 Thu 2020-06-18 05:36:17 UTC; 24s ago Docs: man:php-fpm7.4(8) Process: 33932 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/7.4/fpm/pool.d/www.conf 74 (code=exited,> Main PID: 33919 (php-fpm7.4) Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec" Tasks: 3 (limit: 1119) Memory: 11.3M CGroup: /system.slice/php7.4-fpm.service ├─33919 php-fpm: master process (/etc/php/7.4/fpm/php-fpm.conf) ├─33930 php-fpm: pool www └─33931 php-fpm: pool www -----
Now configure the PHP INI file as shown below.
# Update PHP Configuration sudo nano /etc/php/7.4/fpm/php.ini
# Update cgi.fix_pathinfo=0
# Reload PHP FPM sudo systemctl reload php7.4-fpm # OR # Restart PHP FPM sudo systemctl restart php7.4-fpm
# Restart Nginx sudo systemctl restart nginx
We can enable PHP for Nginx by disabling the default server block and adding the server block as shown below. Also, make sure to replace the example.com with your domain name.
# Disabled default server block sudo unlink /etc/nginx/sites-enabled/default
# Create Server Block sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com
# Update Server Block sudo nano /etc/nginx/sites-available/example.com
# Update Server Block server { listen 80; server_name example.com; # Use localhost to access it on local system
root /var/www/example.com/html;
index index.html index.htm index.php;
location / {
try_files $uri $uri/ =404;
} location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; }
location ~ /\.ht {
deny all; } }
# Save and exit the editor
# Create Symlink sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
# Reload Nginx sudo systemctl reload nginx
Now we will create our first PHP program and execute it to print PHP info.
# Create HTML directory sudo mkdir -p /var/www/example.com/html
# Create Welcome File sudo nano /var/www/example.com/html/index.php
# Content <!DOCTYPE html> <html lang="en"> <head> <title>Hello LEMP</title> </head> <body> <h1>LEMP Stack.</h1> </body> </html>
# Save and exit the editor
Now if you open the URL http://example.com or http://localhost using the Browser, it should show the welcome message - LEMP Stack. We will create another PHP file to print the PHP info as shown below.
# Create Info File sudo nano /var/www/example.com/html/info.php
# Content
<?php echo phpinfo();
# Save and exit the editor
Now open the PHP file using the URL http://example.com/info.php or http://localhost/info.php. It should show the output as shown in Fig 2.
Now remove the info.php for security purposes.
The above-mentioned commands install PHP 7.4 with the generic extensions required by most of the applications. You can also follow How To Install PHP For Nginx On Ubuntu 20.04 LTS to learn more about installing PHP 7 for Nginx.
Install phpMyAdmin On Ubuntu 20.04 LTS (Optional)
This section provides the steps required to install phpMyAdmin on Ubuntu 20.04 LTS. It's optional to install phpMyAdmin as part of the LEMP stack set up. The MySQL server can be accessed directly using the SSH. We can also access MySQL using PHPStorm and SQLYog using SSH and HTTP Tunneling.
Before we start installing phpMyAdmin, we must disable the Validate Password Plugin of MySQL as shown below.
# Login to MySQL sudo mysql # OR mysql -u root -p
# Disabled Validate Password Plugin mysql> UNINSTALL COMPONENT "file://component_validate_password"; mysql> exit;
Now start installing phpMyAdmin on Ubuntu by executing the commands as shown below.
# Install phpMyAdmin sudo apt install phpmyadmin php-mbstring php-gd php-zip php-json
The installation process asks to choose the web server as shown in Fig 3.
The install process asks to create the storage database of phpMyAdmin. You may either skip it or install it. In case you have opted to install the database by choosing Yes as shown in Fig 4, it will ask for the password and confirm password as shown in Fig 5 and Fig 6.
This creates the database of phpMyAdmin and also completes the installation. We can use phpMyAdmin with and without its database. Now again enable the Validate Password Plugin as shown below.
# Login to MySQL sudo mysql # OR mysql -u root -p
# Enable Validate Password Plugin mysql> INSTALL COMPONENT "file://component_validate_password"; mysql> exit;
The installation process installs phpMyAdmin at /usr/share/phpmyadmin and generates the configuration files at /etc/phpmyadmin.
Now, enable the PHP module mbstring, configure phpMyAdmin as sub-directory, and reload the Nginx Web Server as shown below.
# Enable PHP module mbstring sudo phpenmod mbstring
# Configure phpMyAdmin sudo ln -s /usr/share/phpmyadmin /var/www/example.com/html/phpmyadmin sudo chmod 775 -R /usr/share/phpmyadmin/ sudo chown root:www-data -R /usr/share/phpmyadmin/
# Reload Nginx sudo systemctl reload nginx
The above-mentioned commands enable phpMyAdmin as a sub-domain to the primary domain using the alias phpmyadmin. We can access the same on the browser using the URLs as shown below.
Localhost - http://localhost/phpmyadmin OR http://127.0.0.1/phpmyadmin
Remote/Production Server - http://www.example.com/phpmyadmin OR http://xx.xx.xxx.xxx/phpmyadmin - Replace www.example.com with your server's domain address or xx.xx.xxx.xxx with your server's IP address.
Now secure the phpMyAdmin installation using the in-built security feature of Nginx Web Server i.e. Basic Authentication. After enabling the Basic Authentication, the browser will prompt for username and password to access the phpMyAdmin web interface. We can enable the Basic Authentication as shown below.
# Install Apache Utils sudo apt install apache2-utils
# Configure phpMyAdmin - Update server block sudo nano /etc/nginx/sites-available/example.com
# Update Server Block server { listen 80; server_name example.com; # Use localhost to access it on local system
root /var/www/example.com/html;
index index.html index.htm index.php;
location / {
try_files $uri $uri/ =404;
}
location /phpmyadmin {
auth_basic "Restricted";
auth_basic_user_file /etc/phpmyadmin/.htpasswd;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; }
location ~ /\.ht { deny all; } }
# Save and exit the editor
# Reload Nginx sudo systemctl reload nginx
The server block with the basic auth configurations makes sure that the web interface of phpMyAdmin is accessible only if the valid username and password are specified in the auth user file i.e. .htpasswd file. We can add username and password to the .htpasswd as shown below.
# Make directory sudo mkdir -p /etc/phpmyadmin
# Generate Username Password pair sudo htpasswd -c /etc/phpmyadmin/.htpasswd username
# Output New password: Re-type new password: Adding password for user username
We can add more users to the htpasswd file without using the -c argument. Now if we try to access phpMyAdmin, it will show a prompt asking for username and password.
Summary
This tutorial provided the steps required to install and set up the LEMP stack using Ubuntu 20.04 LTS, Nginx 1.17, MySQL 8, and PHP 7.4.