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

How to Install Nginx with PHP and MySQL (LEMP Stack) on Ubuntu 22.04

The LEMP software stack is a group of open-source software that enables a server to host dynamic websites and apps written in PHP. It is an acronym for Linux, Nginx (pronounced as Engine-X), MySQL, and PHP.

This guide will show you to install a LEMP Stack on an Ubuntu 22.04 server. You will also learn to install applications like phpMyAdmin.

Prerequisites

  • A server running Ubuntu 22.04.
  • A non-root user with sudo privileges.
  • Uncomplicated Firewall(UFW) is enabled and running.
  • Everything is updated.

    $ sudo apt update && sudo apt upgrade
    

Step 1 - Configure Firewall

The first step before installing any packages is to configure the firewall to allow HTTP and HTTPS connections.

Check the status of the firewall.

$ sudo ufw status

You should see something like the following.

Status: active

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

Allow HTTP and HTTPs ports.

$ sudo ufw allow http
$ sudo ufw allow https

Check the status again to confirm.

$ sudo ufw status
Status: active

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

Step 2 - Install PHP

Ubuntu 22.04 ships with PHP 8.1 by default. You can install it by running the following command.

$ sudo apt install php-fpm php-cli php-mysqlnd php-mbstring php-xml php-gd

We have installed PHP's MySQL, CLI, GD, Mbstring, and XML extensions. You can install any extra extensions as per your requirements.

To always stay on the latest version of PHP or if you want to install multiple versions of PHP, add Ondrej's PHP repository.

Add Ondrej's PHP repository.

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

Now, you can install any version of PHP.

$ sudo apt install php8.0-fpm php8.0-cli

Check the version of PHP installed.

$ php --version
PHP 8.1.2 (cli) (built: Jun 13 2022 13:52:54) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.2, Copyright (c), by Zend Technologies

Step 3 - Install MySQL

Ubuntu 22.04 ships with the latest version of MySQL. You can install it with a single command.

$ sudo apt install mysql-server

Check the version of MySQL.

$ mysql --version
mysql  Ver 8.0.29-0ubuntu0.22.04.2 for Linux on x86_64 ((Ubuntu))

This step is necessary for MySQL versions 8.0.28 and above. Enter the MySQL Shell.

$ sudo mysql

Run the following command to set the password for your root user. Make sure it has a mix of numbers, uppercase, lowercase, and special characters.

mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'YourPassword12!';

Exit the shell.

mysql> exit

Run the MySQL secure install script.

$ sudo mysql_secure_installation

You will be asked to install the Validate Password Component. It checks the strength of passwords used in MySQL. Press Y to install it.

Next, you will be asked to set the level of the password validation policy. Choose 2 as it is the strongest one.

Next, enter your root password. Press N to refuse to change it.

Press Y to remove anonymous users, disallow remote root logins, remove the test database, and reload the privilege tables.

Step 4 - Configure MySQL

Log in to the MySQL shell. Enter your root password when prompted.

$ sudo mysql -u root -p

Create a sample database.

mysql> CREATE DATABASE exampledb;

Create an SQL user account.

mysql> CREATE USER 'exampleuser'@'localhost' IDENTIFIED BY 'YourPassword2!';

Grant all privileges on the database to the user.

mysql> GRANT ALL PRIVILEGES ON exampledb.* TO 'exampleuser'@'localhost';

Flush user privileges.

mysql> FLUSH PRIVILEGES;

Exit the shell.

mysql> exit

Let us log in again to the MySQL shell using the newly created user.

$ sudo mysql -u exampleuser -p

Create a test table.

mysql> CREATE TABLE exampledb.name_list ( sno INT AUTO_INCREMENT, content VARCHAR(255), PRIMARY KEY(sno) );

Insert test data.

mysql> INSERT INTO exampledb.name_list (content) VALUES ("Navjot");

Repeat the above command separate times to add more entries. Run the following command to check the contents of the table.

mysql> SELECT * FROM exampledb.name_list;

You will receive the following output.

+-----+---------+
| sno | content |
+-----+---------+
|   1 | Navjot  |
|   2 | Adam    |
|   3 | Josh    |
|   4 | Peter   |
+-----+---------+
4 rows in set (0.00 sec)

Exit the MySQL shell.

mysql> exit

Step 5 - Install Nginx

Ubuntu 22.04 ships with an older version of Nginx. To install the latest version, you need to download the official Nginx repository.

Import Nginx's signing key.

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

Add the repository for Nginx's stable version.

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

Update the system repositories.

$ sudo apt update

Install Nginx.

$ sudo apt install nginx

Verify the installation.

$ nginx -v
nginx version: nginx/1.22.0

Step 6 - Configure PHP-FPM

Open php.ini for editing.

$ sudo nano /etc/php/8.1/fpm/php.ini

To set file upload sizes, change the values of the upload_max_filesize and post_max_size variables.

upload_max_filesize = 50M
...
post_max_size = 50M

Configure PHP's memory limit depending upon your server resources and requirements.

memory_limit = 256M

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

Open the file /etc/php/8.0/fpm/pool.d/www.conf.

$ sudo nano /etc/php/8.0/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.
user = nginx
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 php8.1-fpm

Step 7 - Install phpMyAdmin

Download phpMyAdmin's archive file for the English language.

$ wget https://files.phpmyadmin.net/phpMyAdmin/5.2.0/phpMyAdmin-5.2.0-english.tar.gz

Create a public directory for the site.

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

Extract the archive to the public directory.

$ sudo tar -xzvf phpMyAdmin-5.2.0-english.tar.gz -C /var/www/example.com

Switch to the public directory.

$ cd /var/www/html/example.com

Rename the extracted directory to something obscure to improve security.

$ sudo mv phpMyAdmin-5.2.0-english sm175

Step 8 - Configure phpMyAdmin

Copy the sample configuration file.

$ sudo cp sm175/config.sample.inc.php sm175/config.inc.php

Open the configuration file for editing.

$ sudo nano sm175/config.inc.php

Find the line $cfg['blowfish_secret'] = ''; and enter a 32-character random string for cookie-based authentication.

You can use phpSolved's online blowfish generator or do it via the command line.

Copy the value and paste it as shown.

$cfg['blowfish_secret'] = 'Tc/HfLPBOAPxJ-rhQP}HJoZEK69c3j:m';

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

Change the ownership of the site and phpMyAdmin to the Nginx server.

$ sudo chown -R nginx:nginx /var/www/html/example.com

Delete the phpMyAdmin's setup directory.

$ sudo rm -rf /var/www/html/example.com/sm175/setup

Step 9 - Configure Opcache

Opcache is PHP's caching system. It works by saving precompiled script bytecode in the memory, so every time a user visits a page, it loads faster. Opcache is installed by default. To verify, check the PHP version.

$ php --version
PHP 8.1.2 (cli) (built: Jun 13 2022 13:52:54) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.2, Copyright (c), by Zend Technologies

This tells us that Opcache is installed and available. In case, it doesn't show up here, you can install it manually by running the following command.

$ sudo apt install php-opcache

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

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

The following settings should get you started with using Opcache and are generally recommended for good performance. You can enable a configuration by uncommenting it by removing the semi-colon in front of it.

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.

Step 10 - Install Certbot for SSL

We need to install Certbot to generate free SSL certificates offered by Let's Encrypt.

You can either install Certbot using Ubuntu's repository or grab the latest version using the Snapd tool. We will be using the Snapd version.

Ubuntu 22.04 comes with Snapd installed by default. Run the following commands to ensure that your version of Snapd is up to date.

$ sudo snap install core
$ sudo snap refresh core

Install Certbot.

$ sudo snap install --classic certbot

Use the following command to ensure that the Certbot command can be run by creating a symbolic link to the /usr/bin directory.

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Step 11 - Test a demo site

Create the site

Create and open a test page for editing.

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

Paste the following code in it.

<?php
$user = "exampleuser";
$password = "YourPassword2!";
$database = "exampledb";
$table = "name_list";

try {
    $db = new PDO("mysql:host=localhost;dbname=$database", $user, $password);
    echo "<h2>Members List</h2><ol>"; 
    foreach($db->query("SELECT content FROM $table") as $row) {
        echo "<li>" . $row['content'] . "</li>";
    }
    echo "</ol>";
}   catch (PDOException $e) {
    print "Error!: " . $e->getMessage() . "<br/>";
    die();
}

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

Create an SSL Certificate

Run the following command to generate an SSL Certificate.

$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d example.com

The above command will download a certificate to the /etc/letsencrypt/live/example.com directory on your server.

Generate a Diffie-Hellman group certificate.

$ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

Open the file /etc/letsencrypt/renewal/example.com.conf for editing.

$ sudo nano /etc/letsencrypt/renewal/example.com.conf

Paste the following code at the bottom.

pre_hook = systemctl stop nginx
post_hook = systemctl start nginx

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

We have generated the SSL certificate using the standalone option of Certbot. It runs its web server to create the certificate which means Nginx should be shut off during the renewal. The pre_hook and post_hook commands run before and after the renewal to automatically shut and restart the Nginx server thereby requiring no manual intervention.

To check whether the SSL renewal is working fine, do a dry run of the process.

$ sudo certbot renew --dry-run

If you see no errors, you are all set. Your certificate will renew automatically.

Configure Nginx

Create and open the file /etc/nginx/conf.d/example.conf for editing.

$ sudo nano /etc/nginx/conf.d/example.conf

Paste the following code in it.

server {
    listen       443 ssl http2;
    listen       [::]:443 ssl http2;
    server_name  example.com;

    access_log  /var/log/nginx/example.com.access.log;
    error_log   /var/log/nginx/example.com.error.log;

    ssl_certificate      /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    ssl_session_timeout  5m;
    ssl_session_cache shared:MozSSL:10m;
    ssl_session_tickets off;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    root /var/www/html/example.com;

    index index.php index.html;

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

    # Pass PHP Scripts To FastCGI Server
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

# enforce HTTPS
server {
    listen       80;
    listen       [::]:80;
    server_name  example.com;
    return 301   https://$host$request_uri;
}

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

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

$ sudo nano /etc/nginx/nginx.conf

Add the following line before the line include /etc/nginx/conf.d/*.conf;.

server_names_hash_bucket_size  64;

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

Verify your Nginx configuration.

$ sudo nginx -t

If you see no errors, it means you are good to go. Start the Nginx server.

$ sudo systemctl start nginx

Load your website by visiting https://example.com in your browser and you will see the following page.

LEMP Test Site Output

You can access your phpMyAdmin installation by visiting the URL https://example.com/sm175 in your browser. You can either enter your root user or the user created before to log in.

Conclusion

This concludes our tutorial where you learned how to set up a LEMP stack on Ubuntu 22.04 server and made a demo site. If you have any questions, post them in the comments below.

Share this page:

2 Comment(s)