The LEMP stack (Linux, Nginx, MySQL, PHP) is a collection of software used to serve dynamic web pages and PHP applications.
It consists of a Linux operating system, the Nginx (pronounced “Engine-X”) web server, a MySQL database for data storage, and PHP for handling dynamic content.
This guide will walk you through setting up a LEMP stack on a server running Ubuntu 22.04. With Ubuntu managing the Linux component, we’ll cover how to configure each of the remaining parts of the stack.
Prerequisites #
- Ubuntu 22.04
- A user account with admin or sudo access
Step 1: Installing the Nginx Web Server #
Nginx is a popular, efficient web server that we’ll use to serve pages to our visitors. Begin by updating your server’s package index:
sudo apt update

Then install Nginx:
sudo apt install nginx
Press Y and Enter to confirm the installation.

Allowing Nginx Through the Firewall #
If you’re using UFW (Uncomplicated Firewall), you’ll need to allow HTTP traffic through it. Check available UFW application profiles with:
sudo ufw app list
Enable only HTTP traffic on port 80, as we’re not configuring SSL here:
sudo ufw allow 'Nginx HTTP'

Verify the firewall status to confirm:
sudo ufw status
To verify that Nginx is running, open your server’s public IP or domain in a web browser. If unsure about your public IP, run one of the following commands to see it.
$ ip addr show
$ hostname -I
In your browser, navigate to:
http://server_domain_or_ip_address
This should display the Nginx default landing page.

Step 2: Installing MySQL #
With Nginx installed, we’ll move on to installing MySQL, which will manage and store site data.
You can also use MariaDB. Please refer to these steps on how to install MariaDB database on Ubuntu
For MySQL, run:
sudo apt install mysql-server
After installing MySQL, it is a good idea to run a security script included with it
sudo mysql_secure_installation
You’ll be prompted to configure the VALIDATE PASSWORD PLUGIN—type Y for yes or any other key to continue without enabling it.
If you select “yes,” you’ll be prompted to choose a level of password validation. I prefer that you choose level 1.
It is fine to select yes fort he rest of the prompts, but then you can also choose differently according to your preference.

Once done, check if you can log in to the MySQL console:
sudo mysql
This command connects to the MySQL server using the administrative database user root, indicated by sudo. The expected output is as follows:

You may then run the command below to exit the console;
exit

Step 3: Installing PHP #
Next, install PHP to generate dynamic content. Nginx uses PHP-FPM (FastCGI Process Manager) for this purpose. Install PHP and the PHP MySQL extension with:
sudo apt install php8.1-fpm php-mysql
Press Y and Enter to confirm.
Step 4: Configuring Nginx to Use PHP Processor #
We’ll configure Nginx to handle PHP requests. By default, Nginx uses the directory /var/www/html
for its root web directory. We’ll create a new directory for our website and set up a configuration file.
In the steps below, remember to replace your_domain with your actual domain or subdomain. I will use doll.tke.co.ke
- Create the web root directory:
sudo mkdir /var/www/your_domain
2. Assign ownership of the directory to the current user:
sudo chown -R $USER:$USER /var/www/your_domain
3. Open a new configuration file in the sites-available directory:
sudo vi /etc/nginx/sites-available/your_domain
4. Add the following configuration:
server {
listen 80;
server_name your_domain www.your_domain;
root /var/www/your_domain;
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/php8.1-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}

Below is what each of the directives do
- listen specifies the port.
- root sets the document root directory.
- index prioritizes
index.html
overindex.php
. - server_name specifies the domain names.
- location / verifies file existence, returning a 404 if not found.
- location ~ .php$ handles PHP files via FastCGI.
- location ~ /.ht denies access to
.htaccess
files.
5. Save and close the file (in vi, press Esc, type :wq
, and press Enter).
6. Enable your configuration:
sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/
7. Unlink the default configuration:
sudo unlink /etc/nginx/sites-enabled/default
8. Test your configuration for syntax errors:
sudo nginx -t

9. Reload Nginx to apply the changes:
sudo systemctl reload nginx
10. Add a sample index.html file to verify that your new server block works:
vi /var/www/your_domain/index.html

11. Visit http://your_domain
in your browser to confirm.

Step 5: Testing PHP with Nginx #
To test PHP, create a file called info.php in your document root:
vi /var/www/your_domain/info.php
Add the following code:
<?php
phpinfo();
?>
Access this page by navigating to:
http://your_domain/info.php

After confirming, delete the file to avoid exposing sensitive information:
sudo rm /var/www/your_domain/info.php
Step 6: Testing Database Connection from PHP #
Create a test database and user in MySQL:
Log in to MySQL:
sudo mysql
Create a new database:
- Replace test_db with your preferred database name
CREATE DATABASE test_db;

Add a new user:
- Replace test_user with your preferred user name
CREATE USER 'test_user'@'%' IDENTIFIED WITH mysql_native_password BY 'password';

Note:
When running the command you may get this error ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
In that case, just ensure that your password has a mixture of special characters, numbers, uppercase and lowercase characters, then re-run it.
Grant the user privileges on the new database:
GRANT ALL ON test_db.* TO 'test_user'@'%';

Exit MySQL:
exit
To confirm that the new user has the correct permissions, log into the MySQL console again using the custom user credentials.
Be sure to include the -p
flag in the command, as it will prompt for the password set when creating the “test_user” user.
mysql -u test_user -p
Enter your password and confirm to access test_DB.
You can then check the databases available;
SHOW DATABASES;

Let’s now create a test table named “tech_list.” Execute the following statement from the MySQL console:
CREATE TABLE test_db.tech_list (tech_id INT AUTO_INCREMENT,technology VARCHAR(255),PRIMARY KEY(tech_id));

You may now insert values into the table by running multiple inatances of the command below
INSERT INTO test_db.tech_list (technology) VALUES ("IOT");
INSERT INTO test_db.tech_list (technology) VALUES ("Cloud");
INSERT INTO test_db.tech_list (technology) VALUES ("DevOPs");
INSERT INTO test_db.tech_list (technology) VALUES ("ML");
INSERT INTO test_db.tech_list (technology) VALUES ("Networking");

You could check the entries using the command below;
SELECT * FROM test_db.tech_list;

You may exit the MySQL console using the exit command.
Test MySQL Connection #
To test, create a PHP script to connect to MySQL:
Open a file called testlist.php:
vi /var/www/your_domain/testlist.php
Add PHP code to connect and query. In the code below, remember to replace the dummy database details with your own.
<?php
$conn = new mysqli("localhost", "test_user", "password", "test_db");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT * FROM tech_list";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo $row["tech_id"] . ": Technology: " . $row["technology"] . "<br>";
}
} else {
echo "0 results";
}
$conn->close();
?>

Access this script:
http://your_domain/testlist.php

Author’s final word #
Congratulations! You’ve successfully set up a LEMP stack on Ubuntu 20.04, creating a powerful foundation to host PHP-based applications. This configuration, combining Nginx and MySQL, is optimized for speed and security in dynamic web hosting environments.