How to Install Nginx, MySQL, PHP (LEMP Stack) on Ubuntu 24.04
Introduction
LEMP (Linux, Nginx, MySQL, PHP) is an open-source web application stack that enables the development and deployment of dynamic web applications on a server. Within the stack, Nginx serves the application data stored in MySQL databases while PHP processes the dynamic web application contents on your server.
This article explains how to install the Nginx, MySQL, and PHP (LEMP) stack on a Ubuntu 24.04 server. You will set up a basic web application to test the stack functionalities and secure connections to the server using trusted SSL certificates.
Prerequisites
Before you begin:
Deploy an Ubuntu 24.04 instance on Vultr.
Create a new domain A record pointing to the server IP address. For example,
app.example.com
.Access the server using SSH and log in as a non-root user with sudo privileges.
Install Nginx
Install Nginx.
console$ sudo apt install nginx -y
Start Nginx.
console$ sudo systemctl start nginx
Enable Nginx to start at boot time.
console$ sudo systemctl enable nginx
View the Nginx service status and verify that it's active on your server.
console$ sudo systemctl status nginx
Output:
● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled) Active: active (running) since Mon 2024-06-17 01:35:34 UTC; 1s ago Docs: man:nginx(8) Process: 89789 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Process: 89791 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Main PID: 89792 (nginx) Tasks: 2 (limit: 1061) Memory: 1.8M (peak: 1.9M) CPU: 19ms CGroup: /system.slice/nginx.service ├─89792 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;" └─89793 "nginx: worker process"
Install MySQL
Install the latest MySQL database server package on your server.
console$ sudo apt install mysql-server -y
Start the MySQL service.
console$ sudo systemctl start mysql
Enable the MySQL service to start at boot time.
console$ sudo systemctl enable mysql
View the MySQL service status and verify that it's active on the server.
console$ sudo systemctl status mysql
Output:
● mysql.service - MySQL Community Server Loaded: loaded (/usr/lib/systemd/system/mysql.service; enabled; preset: enabled) Active: active (running) since Fri 2024-06-14 01:49:07 UTC; 26min ago Process: 100059 ExecStartPre=/usr/share/mysql/mysql-systemd-start pre (code=exited, status=0/SUCCESS) Main PID: 100082 (mysqld) Status: "Server is operational" Tasks: 39 (limit: 1061) Memory: 293.9M (peak: 380.0M swap: 66.3M swap peak: 66.4M) CPU: 8.781s CGroup: /system.slice/mysql.service └─100082 /usr/sbin/mysqld
Start the MySQL secure installation script to disable insecure default configurations.
console$ sudo mysql_secure_installation
- Enter Y when prompted to enable the VALIDATE PASSWORD component that ensures strict password policies for the database users.
VALIDATE PASSWORD COMPONENT can be used to test passwords and improve security. It checks the strength of password and allows the users to set only those passwords which are secure enough. Would you like to setup VALIDATE PASSWORD component? Press y|Y for Yes, any other key for No: y
- Enter your desired password strength validation policy level. For example, enter 2 to enable strong password usage on the server.
LOW Length >= 8 MEDIUM Length >= 8, numeric, mixed case, and special characters STRONG Length >= 8, numeric, mixed case, special characters and dictionary file Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG:
- Enter Y and press Enter when prompted to remove anonymous users on your database server.
Remove anonymous users? (Press y|Y for Yes, any other key for No) :
- Enter Y when prompted to Disallow remote login and disable remote access to your database server using the root user account.
Disallow root login remotely? (Press y|Y for Yes, any other key for No) :
- Enter Y when prompted to remove the test database on your server.
Remove test database and access to it? (Press y|Y for Yes, any other key for No) :
- Enter Y when prompted to reload privilege tables. Then press and press Enter to refresh the MySQL privilege tables and apply your configuration changes.
Reload privilege tables now? (Press y|Y for Yes, any other key for No) :
Access the MySQL console to set up a new root user password.
console$ sudo mysql
Modify the root user with a strong password and enable
mysql_native_password
as the default authentication method.sqlmysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'secured_password';
Reload the MySQL Privilege tables to apply changes.
sqlmysql> FLUSH PRIVILEGES;
Exit the MySQL console.
sqlmysql> exit
Restart the MySQL database server.
console$ sudo systemctl restart mysql
Install PHP
PHP is available in the default APT repositories on Ubuntu 24.04. The PHP-FPM (FastCGI Process Manager) package enables PHP to run as a service on your server to interact with other applications. Follow the steps below to install PHP, PHP-FPM, and essential modules such as php-mysql
on your server.
Install PHP and PHP-FPM on your server.
console$ sudo apt install php php-fpm -y
Install essential PHP modules required by most dynamic applications.
console$ sudo apt install php-mysql php-cli -y
The above command installs the following PHP modules:
php-mysql
: Enables PHP to connect to the MySQL database server and perform SQL operations.php-cli
: The PHP Command Line Interface (CLI) allows you to run PHP scripts directly in your server terminal session.
View the installed PHP version on your server.
console$ php -v
Your output should be similar to the one below:
PHP 8.3.6 (cli) (built: Apr 15 2024 19:21:47) (NTS) Copyright (c) The PHP Group Zend Engine v4.3.6, Copyright (c) Zend Technologies with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies
Start the PHP-FPM service depending on your installed version such as
PHP 8.3
.console$ sudo systemctl start php8.3-fpm
Enable PHP-FPM to start at boot time.
console$ sudo systemctl enable php8.3-fpm
View the PHP-FPM service status and verify that it's running.
console$ sudo systemctl status php8.3-fpm
Output:
● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/php8.3-fpm.service; enabled; preset: enabled) Active: active (running) since Mon 2024-06-17 01:29:10 UTC; 8min ago Docs: man:php-fpm8.3(8) Process: 89583 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/8.3/fpm/pool.d/www.conf 83 (code=exited, status=0/SUCCESS) Main PID: 89580 (php-fpm8.3) Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec" Tasks: 3 (limit: 1061) Memory: 7.5M (peak: 8.6M) CPU: 110ms CGroup: /system.slice/php8.3-fpm.service ├─89580 "php-fpm: master process (/etc/php/8.3/fpm/php-fpm.conf)" ├─89581 "php-fpm: pool www" └─89582 "php-fpm: pool www"
Configure PHP-FPM
PHP-FPM (FastCGI Process Manager) runs as a system process on your server using pools to handle all PHP connections on your server. Each PHP-FPM pool includes multiple configuration settings that influence the performance and access to PHP on your server. Follow the steps below to modify your default PHP-FPM pool configurations to enable better resource management and optimization on your server.
View the PHP-FPM unix socket path using the
ss
utility.console$ ss -pl | grep php
Your output should be similar to the one below.
u_str LISTEN 0 4096 /run/php/php8.3-fpm.sock 53024 * 0
/run/php/php8.3-fpm.sock
is the PHP-FPM sock path based on the above output. PHP-FPM communicates with other applications such as Nginx using the Unix socket file or TCP connections on the localhost port9000
.Navigate to the PHP-FPM pool configurations directory.
console$ sudo cd /etc/php/8.3/fpm/pool.d/
Open the default PHP-FPM pool configuration
www.conf
using a text editor such as Nano.console$ sudo nano /etc/php/8.3/fpm/pool.d/www.conf
- Verify the PHP-FPM pool name.
ini[www]
- Find the following user directives and verify that PHP-FPM runs with the web server user
www-data
.
iniuser = www-data group = www-data
Save and close the file.
Within the pool configuration:
pm.max_children
: Sets the maximum number of child PHP processes that simultaneously run on the server to ensure balanced memory and CPU resource usage.pm.start_servers
: Specifies the number of child processes created when PHP starts on the server to provide an initial pool of ready-to-use PHP processes to handle requests. The default value2
is based on the averagemin_spare_servers
andmax_spare_servers
values using the equation ((min_spare_servers
+max_spare_servers
) / 2).pm.min_spare_servers
: Sets the minimum number of idle child processes. The default value1
creates a child process if the number of idle processes is below1
.pm.max_spare_server
: Sets the maximum number of idle child processes. The default value3
stops PHP processes if the number of idle processes exceeds3
.pm.max_requests
: Enables the maximum number of requests each child PHP process can execute.
You can change the PHP-FPM pool values depending on your server specifications and available resources.
Configure Nginx with PHP-FPM
Nginx communicates with PHP using the PHP-FPM unix socket or the TCP port 9000
on your server. By default, Nginx cannot serve dynamic web pages without connecting to PHP-FPM. Follow the steps below to configure Nginx with PHP-FPM to serve web application files on the server.
Create a new
index.php
file in your web root directory/var/www/html
.console$ sudo touch /var/www/html/index.php
Run the following command to add new PHP information contents to the file.
console$ echo "<? phpinfo(); >" > sudo /var/www/html/index.php
Remove the default Nginx configuration.
console$ sudo rm -rf /etc/nginx/sites-enabled/default && sudo rm -rf /etc/nginx/sites-available/default
Create a new Nginx virtual host configuration file such as
app.example.com.conf
depending on your domain or web application name.console$ sudo nano /etc/nginx/sites-available/app.example.com.conf
Add the following configurations to the file. Replace
app.example.com
with your actual domain name.nginxserver { 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.3-fpm.sock; } }
Save and close the file.
The above Nginx configuration creates a new virtual host on the server that listens for incoming connections on the HTTP port
80
and serves content using the domainapp.example.com
from the/var/www/html
web root directory. Within the configuration:index index.html index.php index.nginx-debian.html;
: Specifies the default file to serve using the main request path.location ~ \.php$ {
: Sets the configurations to apply when a specific request matches a.php
file extension.include snippets/fastcgi-php.conf;
: Enables the predefined FastCGI configuration snippet for Nginx that contains various directives for handling PHP requests.fastcgi_pass unix:/run/php/php8.3-fpm.sock;
: Sets the location of the PHP-FPM socket Nginx should use to communicate with the PHP-FPM service.
Enable the new Nginx virtual host configuration. Replace
app.example.com
with your domain name.console$ sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/app.example.com
Test the Nginx configuration for errors.
console$ 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 apply your configuration changes.
console$ sudo systemctl restart nginx
Allow the HTTP port
80
through the firewall.console$ sudo ufw allow 80/tcp
Access your domain using a web browser such as Chrome and verify that your PHP Information displays.
Secure the Server
Server security is important when deploying dynamic web applications using the LEMP stack on your server. All installed components must accept only internal connection requests on the loopback IP 127.0.0.1
while the Nginx web server listens for connections on the HTTP port 80
and HTTPS port 443
. Follow the sections below to configure the default UFW firewall to allow connection requests and generate trusted SSL certificates to secure your server traffic.
Configure Uncomplicated Firewall (UFW)
Uncomplicated Firewall (UFW) is active and enabled on Ubuntu 24.04 Vultr servers by default. Follow the steps below to configure UFW and allow connections to the Nginx web server.
List all available UFW application profiles.
console$ sudo ufw app list
Verify that the default Nginx profile is available similar to the following output.
Nginx Full Nginx HTTP Nginx HTTPS OpenSSH
Allow the
Nginx Full
profile to enable the HTTP Port80
and HTTPS Port443
through the firewall.console$ sudo ufw allow 'Nginx Full'
Reload UFW to apply the firewall changes.
console$ sudo ufw reload
View the firewall status to verify that the new rules are available.
console$ sudo ufw status
You should see an output like this:
To Action From -- ------ ---- 22/tcp ALLOW Anywhere Nginx Full ALLOW Anywhere 22/tcp (v6) ALLOW Anywhere (v6) Nginx Full (v6) ALLOW Anywhere (v6)
Generate Let's Encrypt SSL Certificates
Install the Certbot Let's Encrypt client application plugin for Nginx.
console$ sudo apt install python3-certbot-nginx
Request a new Let's Encrypt SSL certificate on your server. Replace
app.example.com
with your domain andhello@example.com
with your actual email address.console$ sudo certbot --nginx --agree-tos --redirect --email hello@example.com -d app.example.com
Verify that Certbot auto-renews the SSL certificate upon expiry.
console$ sudo certbot renew --dry-run
Restart Nginx to apply the configuration changes.
console$ sudo systemctl restart nginx
Test the Installation
Nginx, MySQL, and PHP (LEMP) continuously intercommunicate with your web applications to deliver dynamic contents on your server. Follow the steps below to set up a sample application that displays a Greetings from Vultr
message from a MySQL database on your server.
Log in to MySQL as the
root
user.console$ mysql -u root -p
Enter the
root
user password you set earlier to access the MySQL console.Create a new sample database
vultr_db
.sqlmysql> CREATE DATABASE vultr_db;
Switch to the database.
sqlmysql> USE vultr_db;
Create a new database user
db_user
with a strong password.sqlmysql> CREATE USER 'db_user'@'localhost' IDENTIFIED BY 'strong-password';
Grant the user full privileges to the
vultr_db
database.sqlmysql> GRANT ALL PRIVILEGES ON vultr_db.* TO 'db_user'@'localhost';
Reload the MySQL privileges table to apply the new user changes.
sqlmysql> FLUSH PRIVILEGES;
Create a new table
VultrDocs
with two columns. Theid
column works as the primary key, andmessage
contains VARCHAR strings.sqlmysql> CREATE TABLE IF NOT EXISTS VultrDocs ( id INT AUTO_INCREMENT PRIMARY KEY, message VARCHAR(255) NOT NULL );
Insert new data to the
message
column in theVultrDocs
table with the stringGreetings from Vultr
.sqlmysql> INSERT INTO VultrDocs (message) VALUES ('Greetings from Vultr');
View the table data to verify that the new string is available.
sqlmysql> SELECT * from VultrDocs;
Output:
+----+----------------------+ | id | message | +----+----------------------+ | 1 | Greetings from Vultr | +----+----------------------+ 1 row in set (0.00 sec)
Exit the MySQL console.
sqlmysql> exit;
Create a new
test.php
file in your/var/www/html/
web root directory using a text editor such as Nano.console$ sudo nano /var/www/html/test.php
Add the following contents to the file.
php<?php $servername = "localhost"; $username = "db_user"; $password = "db_password"; $dbname = "vultr_db"; // Create database connection $conn = new mysqli($servername, $username, $password, $dbname); // Check database connection if ($conn->connect_error) { die("Database Connection Failed." . $conn->connect_error); } // Retrieve the record from VultrDocs Table $sql = "SELECT message FROM VultrDocs"; $result = $conn->query($sql); if ($result->num_rows > 0) { $row = $result->fetch_assoc(); echo "<h1>" . $row["message"]. "</h1>"; } else { echo "<h1>No message found.</h1>"; } $conn->close();
Save and close the file.
The above PHP application code connects to your MySQL database and displays the
Greetings from Vultr
string from your samplevultr_db
database when the connection is successful.Access your domain in a new web browser window using the
/test.php
path.https://app.example.com/test.php
Verify that your
Greetings from Vultr
string displays in your browser window.
Conclusion
You have deployed and secured the Nginx, MySQL and PHP (LEMP) stack on your Ubuntu 24.04 server. You can create and securely deliver multiple web applications depending on your server configurations. For more configuration options, visit the following official documentation resources: