A LEMP stack combines Linux, Nginx, MariaDB or MySQL, and PHP-FPM to run dynamic websites and PHP applications. On Ubuntu 24.04, the default repositories provide Nginx, MariaDB 10.11, and PHP 8.3, so you can build the complete stack without adding third-party package repositories.
In this tutorial, you will install each component, create an isolated database user, configure an Nginx server block, connect Nginx to PHP-FPM, and verify the stack with a temporary test page. The result is a clean foundation for WordPress, Laravel, Drupal, and other PHP workloads.
Step 1 — Update Ubuntu and Install Nginx
Refresh the package index and install available security updates:
sudo apt update sudo apt upgrade -y
Install Nginx:
sudo apt install -y nginx
Enable the service at boot and confirm that it is running:
sudo systemctl enable --now nginx sudo systemctl status nginx --no-pager
The status should show active (running).
If UFW is enabled, allow web traffic:
sudo ufw allow 'Nginx Full' sudo ufw status
Open your server's public IP address in a browser:
http://your_server_ip
You should see the default Nginx page. For a deeper explanation of site configuration, see How to Install Nginx on Ubuntu 24.04.
Step 2 — Install and Secure MariaDB
Install MariaDB from the Ubuntu 24.04 repositories:
sudo apt install -y mariadb-server
Start the service and verify its status:
sudo systemctl enable --now mariadb sudo systemctl status mariadb --no-pager
Run MariaDB's security helper:
sudo mariadb-secure-installation
On current MariaDB packages for Ubuntu, the local root database account normally uses Unix socket authentication. Keep Unix socket authentication enabled unless you have a specific operational reason to replace it with a database password.
Recommended choices are:
- Keep Unix socket authentication for the local root account.
- Remove anonymous users.
- Disallow remote root login.
- Remove the test database.
- Reload privilege tables when prompted.
Confirm that local administrative access works:
sudo mariadb
Inside the MariaDB prompt, check the installed version:
SELECT VERSION(); EXIT;
Applications should never connect as the MariaDB root user. You will create a dedicated database account later in the tutorial.
Step 3 — Install PHP-FPM and Common Extensions
Ubuntu 24.04 uses PHP 8.3 as its default PHP branch. Install PHP-FPM and commonly required modules:
sudo apt install -y \ php-fpm \ php-mysql \ php-cli \ php-curl \ php-gd \ php-intl \ php-mbstring \ php-xml \ php-zip \ php-bcmath
Check the active PHP version:
php -v
Verify the PHP-FPM service:
sudo systemctl status php8.3-fpm --no-pager
Confirm the Unix socket exists:
ls -l /run/php/php8.3-fpm.sock
If a future Ubuntu update changes the default PHP branch, use the version shown by php -v in the service name, socket path, and configuration paths below.
Step 4 — Create a Database and Application User
Generate a strong database password and save it in your password manager:
openssl rand -base64 24
Open the MariaDB shell:
sudo mariadb
Create a database and a local-only application user. Replace use_a_unique_password with the generated password:
CREATE DATABASE example_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'example_user'@'localhost' IDENTIFIED BY 'use_a_unique_password'; GRANT ALL PRIVILEGES ON example_app.* TO 'example_user'@'localhost'; EXIT;
The account is restricted to localhost, so it cannot authenticate from another machine. Do not expose MariaDB port 3306 publicly for a single-server LEMP deployment.
Test the application account:
mariadb -u example_user -p example_app
Enter the password when prompted, then run:
SELECT DATABASE(), CURRENT_USER(); EXIT;
Step 5 — Create the Website Directory
This tutorial uses example.com. Replace it with your actual domain throughout the remaining commands.
Create the document root:
sudo mkdir -p /var/www/example.com/public sudo chown -R "$USER":www-data /var/www/example.com sudo find /var/www/example.com -type d -exec chmod 755 {} \; sudo find /var/www/example.com -type f -exec chmod 644 {} \;
Create a simple HTML page:
cat <<'EOF' | sudo tee /var/www/example.com/public/index.html >/dev/null <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>LEMP stack ready</title> </head> <body> <h1>Nginx is serving example.com</h1> </body> </html> EOF
Step 6 — Configure the Nginx Server Block
Create a site configuration:
sudo nano /etc/nginx/sites-available/example.com
Add the following configuration:
server { listen 80; listen [::]:80; server_name example.com www.example.com; root /var/www/example.com/public; index index.php index.html; access_log /var/log/nginx/example.com.access.log; error_log /var/log/nginx/example.com.error.log; location / { try_files $uri $uri/ =404; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock; } location ~ /\.ht { deny all; } }
Enable the site:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
Disable the default site if it is still enabled:
sudo rm -f /etc/nginx/sites-enabled/default
Test the configuration before reloading Nginx:
sudo nginx -t sudo systemctl reload nginx
If nginx -t reports an error, do not reload the service until the configuration issue is fixed.
Step 7 — Verify PHP Processing
Create a temporary PHP test page:
cat <<'EOF' | sudo tee /var/www/example.com/public/info.php >/dev/null <?php phpinfo(); EOF
Open the page in your browser:
http://example.com/info.php
Check that:
Server APIshowsFPM/FastCGI.mysqliorPDOMySQL support is loaded.- The PHP version matches the installed PHP-FPM service.
Delete the page immediately after testing because phpinfo() exposes server and module details:
sudo rm /var/www/example.com/public/info.php
Verify the remaining site returns a successful response:
curl -I http://example.com
Step 8 — Check Services and Logs
Check the three services together:
systemctl is-active nginx mariadb php8.3-fpm
Each line should return active.
Useful logs include:
sudo journalctl -u nginx -n 50 --no-pager sudo journalctl -u mariadb -n 50 --no-pager sudo journalctl -u php8.3-fpm -n 50 --no-pager sudo tail -n 50 /var/log/nginx/example.com.error.log
For a production application, measure real PHP worker memory and request behavior before changing PHP-FPM process limits. Fixed pm.max_children values are not portable because plugins, frameworks, traffic patterns, and database workloads consume different amounts of memory.
Step 9 — Add HTTPS and Production Protection
Before entering real credentials or serving users, secure the domain with Let's Encrypt and Certbot.
Also plan the following:
- Keep Ubuntu, Nginx, PHP, and MariaDB packages patched.
- Back up both application files and the database.
- Store secrets outside the public document root.
- Keep database port
3306closed unless a private architecture requires remote access. - Use a dedicated database user for each application.
- Add monitoring before the workload becomes business-critical.
Raff Linux VMs start at $8.49 per month with 2 vCPU, 2 GB RAM, 40 GB NVMe storage, and 3 Gbps unmetered bandwidth. For a LEMP stack running Nginx, PHP-FPM, and MariaDB on the same VM, the 2 vCPU and 4 GB RAM plan at $13.99 per month provides more practical memory headroom.
Conclusion
You now have a working LEMP stack on Ubuntu 24.04 with Nginx serving the site, PHP-FPM processing PHP requests, and MariaDB using a dedicated local application account.
The next logical deployment is WordPress on Ubuntu 24.04 with Nginx. For applications that need independent database scaling, automated maintenance, or easier failover, compare this self-hosted setup with managed versus self-hosted databases.