Introduction
A LEMP stack is one of the most popular open-source web server configurations for hosting dynamic websites and web applications. LEMP stands for Linux, Engine-X (Nginx), MariaDB (or MySQL), and PHP — four components that work together to serve, process, and store web content.
Nginx handles incoming HTTP requests and serves static files with exceptional speed. When a request requires dynamic processing — like loading a WordPress page or running a Laravel application — Nginx forwards it to PHP-FPM, which executes the PHP code. PHP communicates with MariaDB to read and write data, then returns the generated HTML to Nginx, which delivers it to the visitor's browser.
In this tutorial, you will install and configure each component of the LEMP stack on your Raff Ubuntu 24.04 VM. You will set up Nginx as the web server, install MariaDB with a secure initial configuration, install PHP-FPM with common extensions, and verify the complete stack by serving a PHP info page. By the end, you will have a production-ready foundation for hosting PHP-based applications.
Step 1 — Updating the System and Installing Nginx
Start by updating the package index and upgrading installed packages to their latest versions. This ensures you get the most recent software versions and security patches.
bashsudo apt update && sudo apt upgrade -y
Install Nginx from the default Ubuntu repositories:
bashsudo apt install -y nginx
Nginx starts automatically after installation. Verify it is running:
bashsudo systemctl status nginx
You should see active (running) in the output. If you have configured UFW on your server, allow HTTP and HTTPS traffic:
bashsudo ufw allow 'Nginx Full'
Open your server's IP address in a browser:
http://your_server_ip
You should see the default Nginx welcome page. This confirms Nginx is installed, running, and accessible from the internet.
Check the installed Nginx version:
bashnginx -v
Ubuntu 24.04 ships with Nginx 1.24.x or later from its default repositories.
Step 2 — Installing MariaDB
MariaDB is a community-developed fork of MySQL that offers improved performance and additional features while maintaining full MySQL compatibility. Most PHP applications that support MySQL also support MariaDB without any configuration changes.
Install MariaDB server:
bashsudo apt install -y mariadb-server
Verify MariaDB is running:
bashsudo systemctl status mariadb
Run the security script to remove default insecure settings:
bashsudo mysql_secure_installation
The script will prompt you through several security questions. Here are the recommended answers:
- Enter current password for root: Press Enter (no password is set by default)
- Switch to unix_socket authentication: Type
Y— this is more secure than password-based root login - Change the root password: Type
Yand set a strong password - Remove anonymous users: Type
Y - Disallow root login remotely: Type
Y - Remove test database and access to it: Type
Y - Reload privilege tables now: Type
Y
Warning
Never allow remote root login to your database server. Applications should connect using dedicated database users with limited privileges, not the root account.
Verify you can connect to MariaDB:
bashsudo mariadb
You should see the MariaDB monitor prompt. Check the version:
sqlSELECT VERSION();
Expected output will show MariaDB 10.11.x or later. Type EXIT; to leave the MariaDB shell.
Step 3 — Installing PHP-FPM and Common Extensions
PHP-FPM (FastCGI Process Manager) is the recommended way to run PHP with Nginx. Unlike Apache's mod_php, PHP-FPM runs as a separate process pool that Nginx communicates with via a socket or TCP connection. This architecture is more efficient and allows you to fine-tune PHP process management independently.
Install PHP-FPM and the extensions most commonly required by PHP applications:
bashsudo apt install -y php-fpm php-mysql php-curl php-gd php-intl php-mbstring php-xml php-zip php-bcmath php-cli php-common
Here is what each extension provides:
php-fpm— The FastCGI Process Managerphp-mysql— MariaDB/MySQL database connectivityphp-curl— HTTP client for API callsphp-gd— Image processing (thumbnails, resizing)php-intl— Internationalization functionsphp-mbstring— Multi-byte string handling (UTF-8 support)php-xml— XML parsing and generationphp-zip— ZIP archive handlingphp-bcmath— Arbitrary precision mathematics
Check the installed PHP version:
bashphp -v
Ubuntu 24.04 ships with PHP 8.3.x. Verify PHP-FPM is running:
bashsudo systemctl status php8.3-fpm
Note
If your PHP version differs (e.g., 8.4), adjust the version number in all subsequent commands and configuration paths. Check with php -v to confirm your exact version.
Step 4 — Configuring Nginx to Process PHP
Create an Nginx server block configuration for your site. This example uses example.com as the domain — replace it with your actual domain name or your server's IP address.
Create the web root directory:
bashsudo mkdir -p /var/www/example.com
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com
Create the Nginx server block:
bashsudo nano /etc/nginx/sites-available/example.com
Paste the following configuration:
nginxserver {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Logging
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
}
Key configuration details:
fastcgi_pass unix:/run/php/php8.3-fpm.sock— Connects Nginx to PHP-FPM via a Unix socket, which is faster than TCP for same-server communicationlocation ~ /\.ht— Blocks access to.htaccessfiles, which are Apache-specific and should not be publicly accessible- The security headers protect against common web vulnerabilities like clickjacking and content-type sniffing
Enable the server block by creating a symbolic link:
bashsudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
Optionally remove the default server block to avoid conflicts:
bashsudo rm /etc/nginx/sites-enabled/default
Test the Nginx configuration for syntax errors:
bashsudo nginx -t
If the test passes, reload Nginx:
bashsudo systemctl reload nginx
Step 5 — Creating a Test Database and Database User
Production applications should never use the MariaDB root account. Create a dedicated database and user for your application.
Log in to MariaDB:
bashsudo mariadb
Create a database:
sqlCREATE DATABASE example_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
The utf8mb4 character set supports the full Unicode range, including emojis. Always use utf8mb4 instead of utf8 for new databases.
Create a user with a strong password and grant permissions:
sqlCREATE USER 'example_user'@'localhost' IDENTIFIED BY 'your_strong_password_here';
GRANT ALL PRIVILEGES ON example_db.* TO 'example_user'@'localhost';
FLUSH PRIVILEGES;
The @'localhost' restriction means this user can only connect from the local machine, adding a layer of security.
Verify the user can connect:
sqlEXIT;
bashmariadb -u example_user -p example_db
Enter the password when prompted. If you see the MariaDB prompt, the user and database are configured correctly. Type EXIT; to leave.
Step 6 — Testing PHP Processing and Database Connectivity
Create a PHP test file to verify that Nginx correctly passes requests to PHP-FPM:
bashsudo nano /var/www/example.com/info.php
Add the following content:
php<?php
phpinfo();
Open http://your_server_ip/info.php in your browser. You should see the PHP information page displaying your PHP version, loaded modules, and configuration details.
Verify the following on the PHP info page:
- Server API should show
FPM/FastCGI - mysqli and pdo_mysql should appear in the loaded modules
- mbstring, curl, gd, and xml should be listed
Now test database connectivity. Create a PHP file that connects to MariaDB:
bashsudo nano /var/www/example.com/db_test.php
Add the following content:
php<?php
$host = 'localhost';
$db = 'example_db';
$user = 'example_user';
$pass = 'your_strong_password_here';
try {
$pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8mb4", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Database connection successful. MariaDB version: " . $pdo->query('SELECT VERSION()')->fetchColumn();
} catch (PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
Open http://your_server_ip/db_test.php in your browser. You should see a success message with the MariaDB version number.
Warning
Remove both test files immediately after verification. The phpinfo page exposes sensitive server configuration details, and the database test file contains credentials.
bashsudo rm /var/www/example.com/info.php /var/www/example.com/db_test.php
Step 7 — Tuning PHP-FPM for Production
The default PHP-FPM configuration is conservative. For production workloads on a Raff VM with 4 GB RAM, adjust the process manager settings.
Open the PHP-FPM pool configuration:
bashsudo nano /etc/php/8.3/fpm/pool.d/www.conf
Find and modify these directives:
inipm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 500
pm.max_children— Maximum number of PHP worker processes. Each process uses approximately 30-50 MB of RAM. With 4 GB total and other services running, 20 is a reasonable limit.pm.max_requests— Recycles worker processes after 500 requests to prevent memory leaks from accumulating.
Also adjust PHP settings for better performance:
bashsudo nano /etc/php/8.3/fpm/php.ini
Modify these values:
iniupload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 300
max_input_vars = 5000
Restart PHP-FPM to apply the changes:
bashsudo systemctl restart php8.3-fpm
Verify PHP-FPM restarted without errors:
bashsudo systemctl status php8.3-fpm
Conclusion
You have set up a complete LEMP stack on your Raff Ubuntu 24.04 VM with Nginx serving web content, MariaDB managing databases with a secure configuration, PHP 8.3 with FPM processing dynamic requests, and production-ready tuning for both PHP-FPM and Nginx.
This stack is the foundation for hosting PHP applications including WordPress, Laravel, Drupal, Magento, and many others. From here, you can:
- Install WordPress on top of your LEMP stack
- Secure your site with HTTPS using Let's Encrypt
- Add Redis caching for improved application performance
Raff's NVMe SSD storage gives your LEMP stack fast disk I/O for database queries and file serving, while AMD EPYC processors handle PHP processing efficiently. With hourly billing, you can test your configuration and scale up when traffic grows.