How to Install Nginx, MySQL, PHP (LEMP Stack) on Ubuntu 24.04?
Introduction
Setting up a LEMP stack, which includes Linux, Nginx, MySQL, and PHP, is a foundational step for hosting modern, dynamic web applications. This stack provides a powerful, open-source platform that’s lightweight, efficient, and widely supported across the web hosting industry.
Ubuntu with its long-term support and up-to-date packages, is an excellent choice for deploying the LEMP stack. By combining the speed of the Nginx web server with the reliability of MySQL and the flexibility of PHP, developers can build and scale robust web applications with ease.
In this guide, you’ll learn how to install and configure each component of the LEMP stack on an Ubuntu 24.04 server. We’ll walk through setting up a sample PHP application, connecting it to a MySQL database, and securing the server with an SSL certificate from Let’s Encrypt, ensuring your site is ready for production.
Prerequisites
Before getting started:
- Set up an Ubuntu 24.04 server on Cantech.
- Create an A record for your domain (e.g.,
app.example.com) pointing to your server’s public IP. - Connect via SSH as a non-root user with sudo privileges.
- Update your server packages:
sudo apt update sudo apt upgrade -y
Step 1: Install Nginx Web Server
➞ Install Nginx web server to host fast and secure websites on Ubuntu.
sudo apt install nginx -y
➞ Once the installation is complete, enable Nginx to start automatically at system boot using the command below.
sudo systemctl enable nginx
➞ Next, start the Nginx service using the following command:
sudo systemctl start nginx
➞ Verify Nginx status:
sudo systemctl status nginx
You should see output confirming that the service is active and running.
● 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 Wed 2025-10-15 10:24:17 UTC; 5min ago
Docs: man:nginx(8)
Main PID: 1245 (nginx)
Tasks: 3 (limit: 1152)
Memory: 5.8M
CPU: 110ms
CGroup: /system.slice/nginx.service
├─1245 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
├─1246 nginx: worker process
└─1247 nginx: worker process
Step 2: Installing MySQL
➞ Install the most recent version of the MySQL database server on your system.
sudo apt install mysql-server -y
➞ Start running the MySQL service.
sudo systemctl start mysql
➞ Enable the MySQL service to automatically start on system boot.
sudo systemctl enable mysql
➞ Verify MySQL status:
sudo systemctl status mysql
Output:
● mysql.service - MySQL Community Server
Loaded: loaded (/lib/systemd/system/mysql.service; enabled; preset: enabled)
Active: active (running) since Wed 2025-10-15 11:25:43 UTC; 1min 32s ago
Main PID: 1432 (mysqld)
Status: "Server is operational"
Tasks: 38 (limit: 9442)
Memory: 364.8M
CPU: 1.482s
CGroup: /system.slice/mysql.service
└─1432 /usr/sbin/mysqld
➞ Run the MySQL secure installation tool to remove unsafe default settings and improve your database security.
sudo mysql_secure_installation
Recommended prompts:
- Enable the
VALIDATE PASSWORDplugin. - Choose STRONG policy (
2). - Remove anonymous users.
- Disallow remote root login.
- Remove the test database.
- Reload privilege tables.
➞ Open the MySQL console and create a new password for the root user.
sudo mysql
➞ Set a strong password and update authentication method:
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_secure_password';
➞ Refresh the MySQL privilege tables to make the changes effective.
mysql> FLUSH PRIVILEGES;
➞ Leave the MySQL console.
mysql> exit
➞ Restart MySQL:
sudo systemctl restart mysql
Step 3: Installing PHP
➞ Ubuntu 24.04 includes PHP 8.4 in its repository, offering slight performance improvements over PHP 8.3. Use the command below to install PHP 8.4 along with common extensions.
sudo apt install php8.4 php8.4-fpm php8.4-mysql php8.4-cli -y
The above command installs the following PHP modules:
php-mysql: Allows PHP to communicate with the MySQL database and execute SQL queries.php-cli: Provides the PHP Command Line Interface (CLI) to run PHP scripts directly from the terminal.
➞ Verify PHP version:
php -v
Output:
PHP 8.4.0 (cli) (built: Sep 23 2024 10:15:42) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.0, Copyright (c) Zend Technologies
with Zend OPcache v8.4.0, Copyright (c), by Zend Technologies
➞ Start the PHP-FPM service for your installed version, such as PHP 8.4.
sudo systemctl start php8.4-fpm
➞ Set PHP-FPM to automatically start when the system boots.
sudo systemctl enable php8.4-fpm
➞ Check the PHP-FPM service status to make sure it’s running properly.
sudo systemctl status php8.4-fpm
Output:
● php8.4-fpm.service - The PHP 8.4 FastCGI Process Manager
Loaded: loaded (/lib/systemd/system/php8.4-fpm.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2025-10-15 10:25:47 UTC; 2min ago
Docs: man:php-fpm8.4(8)
Main PID: 15234 (php-fpm8.4)
Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
Tasks: 3 (limit: 1112)
Memory: 8.3M
CPU: 95ms
CGroup: /system.slice/php8.4-fpm.service
├─15234 php-fpm: master process (/etc/php/8.4/fpm/php-fpm.conf)
├─15235 php-fpm: pool www
└─15236 php-fpm: pool www
Step 4: Configure PHP-FPM
➞ Check the PHP-FPM Unix socket path using the ss command.
ss -pl | grep php
Output:
u_str LISTEN 0 128 /run/php/php8.4-fpm.sock 12345 * 1234/php-fpm: master
-
/run/php/php8.4-fpm.sock: Shows the PHP-FPM socket path. -
php-fpm: master: Indicates the PHP-FPM master process is active and listening.
➞ Navigate to PHP-FPM pool config directory:
sudo cd /etc/php/8.4/fpm/pool.d/
➞ Open the default PHP-FPM pool configuration file www.conf in a text editor like Nano.
sudo nano /etc/php/8.4/fpm/pool.d/www.conf
➞ Check the PHP-FPM pool name to confirm it’s correctly configured.
[www]
➞ Check that PHP-FPM runs under the www-data user.
user = www-data group = www-data
Save and close the file.
➞ Adjust process manager settings if needed:
- pm.max_children = 10
- pm.start_servers = 4
- pm.min_spare_servers = 2
- pm.max_spare_servers = 6
- pm.max_requests = 500
You can adjust the PHP-FPM pool settings based on your server’s specs and resources.
Step 5: Configure Nginx to Use PHP-FPM
➞ Create an index.php file inside your web root directory /var/www/html.
sudo touch /var/www/html/index.php
➞ Use the below command to insert PHP info content into the file.
echo "<?php phpinfo(); >" > sudo /var/www/html/index.php
➞ Delete default Nginx configuration file:
sudo rm /etc/nginx/sites-enabled/default sudo rm /etc/nginx/sites-available/default
➞ Create a new server block:
sudo nano /etc/nginx/sites-available/app.example.com.conf
➞ Add the following configuration:
server {
listen 80;
server_name app.example.com;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
}
Save and close the file.
➞ Enable the new Nginx configuration. Replace app.example.com with your actual domain name.
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/app.example.com
➞ Check the Nginx configuration to ensure there are no errors.
sudo nginx -t
Output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
➞ Restart Nginx to load the updated configuration settings.
sudo systemctl restart nginx
➞ Allow HTTP traffic through UFW:
sudo ufw allow 80/tcp
Visit http://app.example.com to confirm PHP is working.
Step 6: Secure Your Server with SSL
When setting up dynamic web applications with the LEMP stack, securing your server is essential. Make sure all components accept only internal requests on 127.0.0.1, while Nginx handles HTTP (port 80) and HTTPS (port 443) traffic. Follow the steps below to configure the UFW firewall and enable SSL certificates for secure connections.
Set Up Uncomplicated Firewall (UFW)
By default, Uncomplicated Firewall (UFW) is enabled and active on Ubuntu 24.04 Cantech servers. Use the steps below to set up UFW and permit connections to the Nginx web server.
➞ Display all available UFW application profiles.
sudo ufw app list
Output:
Nginx Full Nginx HTTP Nginx HTTPS OpenSSH
➞ Permit the Nginx Full profile to open HTTP Port 80 and HTTPS Port 443 through the firewall.
sudo ufw allow 'Nginx Full'
➞ Restart UFW to implement the firewall modifications.
sudo ufw reload
➞ Check the firewall status to confirm that the new rules are active.
sudo ufw status
Output:
To Action From -- ------ ---- 22/tcp ALLOW Anywhere Nginx Full ALLOW Anywhere 22/tcp (v6) ALLOW Anywhere (v6) Nginx Full (v6) ALLOW Anywhere (v6)
Create Let’s Encrypt SSL Certificates
➞ Set up the Certbot Let’s Encrypt client plugin for Nginx.
sudo apt install python3-certbot-nginx -y
➞ Obtain and configure the SSL certificate:
sudo certbot --nginx --agree-tos --redirect --email [email protected] -d app.example.com
➞ Test auto-renewal:
sudo certbot renew --dry-run
➞ Restart Nginx:
sudo systemctl restart nginx
Step 7: Test the LEMP Stack with a Sample App
Nginx, MySQL, and PHP (LEMP) work together seamlessly to provide dynamic content for your web applications. Use the steps below to configure a sample application that shows a ‘Greetings from Cantech‘ message retrieved from a MySQL database on your server.
➞ Access MySQL using the root user account
mysql -u root -p
Use the root user password you previously configured to log into the MySQL console.
➞ Set up a new sample database named cantech_db
mysql> CREATE DATABASE cantech_db;
➞ Select the database for use.
mysql> USE cantech_db;
➞ Set up a new database user named db_user with a secure password.
mysql> CREATE USER 'db_user'@'localhost' IDENTIFIED BY 'strong-password';
➞ Assign full permissions to the user for the cantech_db database.
mysql> GRANT ALL PRIVILEGES ON cantech_db. TO 'db_user'@'localhost';
➞ Refresh the MySQL privileges table to implement the new user modifications.
mysql> FLUSH PRIVILEGES;
➞ Set up a new table named CantechDocs with two columns: an id column serving as the primary key and a message column to store VARCHAR strings.
mysql> CREATE TABLE IF NOT EXISTS CantechDocs (id INT AUTO_INCREMENT PRIMARY KEY, message VARCHAR(255) NOT NULL);
➞ Add a new entry to the message column in the CantechDocs table with the text Greetings from Cantech.
mysql> INSERT INTO CantechDocs (message) VALUES ('Greetings from Cantech');
➞ Check the table data to confirm that the new string has been added.
mysql> SELECT * from CantechDocs;
Output:
+----+------------------------+ | id | message | +----+------------------------+ | 1 | Greetings from Cantech | +----+------------------------+ 1 row in set (0.00 sec)
➞ Close the MySQL console.
mysql> exit;
➞ Generate a new test.php file in the /var/www/html/ web root directory using a text editor like Nano.
sudo nano /var/www/html/test.php
➞ Insert the following content into the file.
<?php
$servername = "localhost";
$username = "db_user";
$password = "strong-password";
$dbname = "cantech_db";
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Database Connection Failed: " . $conn->connect_error);
}
$sql = "SELECT message FROM CantechDocs";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
echo "<h1>" . htmlspecialchars($row["message"]) . "</h1>";
} else {
echo "<h1>No message found.</h1>";
}
$conn->close();
?>
Save and exit.
The PHP application code above establishes a connection to your MySQL database and shows the Greetings from Cantech string from your sample cantech_db database upon successful connection.
➞ Open your domain in a new browser window using the /test.php path.
https://app.example.com/test.php
You should see:
Greetings from Cantech
Conclusion
You’ve successfully installed, configured, and secured a LEMP stack Ubuntu 24.04. With Nginx serving as the web server, MySQL managing your databases, and PHP powering dynamic content, your server is now ready to host modern web applications. Secure your stack further with strong passwords, UFW rules, and regular updates.
Related: