Set Up a LEMP Stack on Ubuntu 24.04: Nginx, MySQL, PHP 8.4 (2026)

Khimananda Oli 8 min read Database
Set Up a LEMP Stack on Ubuntu 24.04: Nginx, MySQL, PHP 8.4 (2026)

By Khimananda Oli | Last reviewed: August 2026

You have a hardened Ubuntu box and now you need somewhere to actually serve a site. A LEMP stack on Ubuntu — Linux, Nginx, MySQL, and PHP — is the standard foundation for WordPress, Laravel, and most PHP applications, and it is fast, lean, and free. This guide installs each piece on Ubuntu 24.04 in the right order, wires Nginx to PHP 8.4 through a FastCGI socket, secures MySQL 8, and verifies the whole chain with a throwaway phpinfo() page. If you have not locked the server down yet, start with my initial Ubuntu server setup guide first, then come back here. Prefer to hand it off? My DevOps and cloud services cover provisioning end to end.

How a request travels through a LEMP stackBrowserclientNginxport 80 / 443PHP-FPMPHP 8.4MySQL 8localhostHTTPFastCGISQLHTML response returns along the same path
In a LEMP stack, Nginx serves static files directly and hands only PHP requests to PHP-FPM, which talks to MySQL 8 on localhost.

What is a LEMP stack and why use it on Ubuntu?

LEMP stands for Linux, (E)Nginx, MySQL, and PHP — the "E" is the phonetic spelling of Nginx ("engine-x"). It is the same idea as the classic LAMP stack, but it swaps Apache for Nginx, which handles high concurrency with a smaller memory footprint because it uses an event-driven model instead of one process per connection. On Ubuntu 24.04 LTS every component installs straight from the official repositories, so you get security patches for years without third-party PPAs.

The key architectural difference from LAMP is how PHP runs. Nginx has no embedded PHP module; instead it forwards dynamic requests to a separate PHP-FPM (FastCGI Process Manager) service over a Unix socket. That separation is a feature — Nginx stays lean and fast at serving static assets, while PHP-FPM manages its own worker pool independently.

The four layers of a LEMP stackNginx — web server, reverse proxy, static filesPHP 8.4-FPM — application runtimeMySQL 8 — relational databaseUbuntu 24.04 LTS (Linux) — the base OSevery layer installs from the official apt repositories
The LEMP stack layers on Ubuntu 24.04 — the Linux base carries Nginx, PHP 8.4-FPM, and MySQL 8, all from apt.

How do you install Nginx, MySQL, and PHP on Ubuntu 24.04?

Start from an updated package index, then install the three services. Ubuntu 24.04 ships PHP 8.3 by default, but PHP 8.4 is available cleanly through the well-maintained ondrej/php PPA, which is the standard source most teams use in 2026:

sudo apt update && sudo apt -y upgrade

sudo apt install -y nginx mysql-server

sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
sudo apt install -y php8.4-fpm php8.4-mysql php8.4-cli php8.4-curl php8.4-mbstring php8.4-xml php8.4-zip

That gives you Nginx, MySQL 8, and PHP 8.4-FPM plus the extensions most applications need. Confirm each service is installed and running before wiring them together:

systemctl status nginx --no-pager
systemctl status mysql --no-pager
systemctl status php8.4-fpm --no-pager

All three should report active (running). If you have the UFW firewall enabled from your initial hardening, open the web ports so Nginx is reachable — leave the MySQL port closed to the internet:

sudo ufw allow 'Nginx Full'
sudo ufw status

The Nginx Full profile opens both port 80 (HTTP) and 443 (HTTPS). Visiting your server's IP in a browser should now show the default Nginx welcome page.

How do you secure MySQL 8 after installing it?

A fresh MySQL 8 install has no root password and a few insecure defaults left over for convenience. The bundled mysql_secure_installation script fixes them interactively:

sudo mysql_secure_installation

Work through the prompts and answer as follows for a production server:

  • Validate password component — enable it (choose Y) and pick at least the MEDIUM policy so weak passwords are rejected.
  • Remove anonymous users — yes; anonymous accounts let anyone connect without credentials.
  • Disallow root login remotely — yes; root should only ever connect from localhost.
  • Remove the test database — yes; it is world-writable by default and serves no purpose in production.
  • Reload privilege tables — yes, so every change takes effect immediately.

On Ubuntu, the MySQL root user authenticates through the auth_socket plugin by default, meaning you log in with sudo mysql and no password. That is fine for administration, but create a dedicated application user rather than letting your app connect as root:

sudo mysql

CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-strong-password-here';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Scoping the grant to appdb.* and binding the user to localhost means a leaked application credential cannot touch other databases or connect from another host.

How do you configure an Nginx server block to run PHP?

An Nginx server block (Apache calls it a virtual host) defines how one site is served. Create a document root, then a config file that serves static files directly and passes .php requests to the PHP-FPM socket. First the web root:

sudo mkdir -p /var/www/example.com/html
sudo chown -R www-data:www-data /var/www/example.com

Now the server block. The critical line is fastcgi_pass, which points Nginx at the PHP 8.4-FPM Unix socket:

sudo tee /etc/nginx/sites-available/example.com > /dev/null <<'EOF'
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/example.com/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}
EOF

Enable the site by symlinking it into sites-enabled, test the configuration syntax, and reload Nginx. Always run nginx -t before reloading — it catches typos before they take the site down:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Here is what each block does:

  • root and index — set the document root and the files Nginx tries as a directory index.
  • location / — the try_files fallback to /index.php is what makes front-controller frameworks like Laravel and WordPress route correctly.
  • location ~ \.php$ — matches PHP files and forwards them over FastCGI to PHP-FPM.
  • location ~ /\.ht — denies access to hidden Apache-style files that occasionally get copied in.
Wiring the Nginx server block to the PHP-FPM socketNginx server blocklocation ~ \.php$ {include fastcgi-php.conf;fastcgi_pass unix:...sock;}PHP 8.4-FPM servicelistens on the socketruns a worker poolexecutes the PHP file/run/php/php8.4-fpm.sock
The fastcgi_pass directive is the single line that connects the Nginx server block to the PHP-FPM socket — mismatch the socket path and PHP files download instead of running.

How do you test that PHP works with Nginx?

With everything wired, confirm the full chain end to end. Create a temporary info.php in the document root that calls phpinfo():

echo "<?php phpinfo(); ?>" | sudo tee /var/www/example.com/html/info.php

Visit http://your-server-ip/info.php (or the domain if DNS is pointed). You should see the PHP 8.4 information page. Scroll down and confirm the mysqli and PDO sections are present — that proves PHP can talk to MySQL. If instead the browser downloads the file, the location ~ \.php$ block or the socket path is wrong.

The phpinfo page exposes your exact versions, module list, and paths, so delete it the moment the test passes — leaving it live is a real information-disclosure risk:

sudo rm /var/www/example.com/html/info.php

Your LEMP stack is now live and serving PHP. The natural next steps are adding a real application and a TLS certificate. For a framework deployment, follow my guide to deploying Laravel on an Ubuntu VPS with Nginx, and to encrypt traffic see how to set up free SSL with Let's Encrypt and Certbot.

Conclusion

A LEMP stack on Ubuntu 24.04 comes down to four moves: install Nginx, MySQL 8, and PHP 8.4-FPM from apt; secure MySQL with mysql_secure_installation and a scoped application user; write a server block whose fastcgi_pass points at the PHP-FPM socket; then verify with a phpinfo page you delete straight after. Do it in that order and you have a fast, patchable foundation for almost any PHP application. Want your stack provisioned, tuned, and monitored for production? See real deployments in my DevOps portfolio or contact me to set it up for you.

Frequently Asked Questions

LEMP stands for Linux, Nginx, MySQL, and PHP. The "E" is the phonetic spelling of Nginx, pronounced "engine-x". It is the Nginx-based alternative to the Apache-based LAMP stack and is a common foundation for PHP applications on Ubuntu.

A LAMP stack uses Apache as the web server, while a LEMP stack uses Nginx. Nginx handles many concurrent connections with lower memory using an event-driven model, and it runs PHP through a separate PHP-FPM service rather than an embedded module like Apache's mod_php.

Use Ubuntu 24.04 LTS. It receives five years of standard security updates, giving you a stable, predictable base, and Nginx, MySQL 8, and PHP all install cleanly from official or well-maintained repositories.

Add the ondrej/php PPA with sudo add-apt-repository ppa:ondrej/php, run sudo apt update, then install php8.4-fpm along with the extensions you need such as php8.4-mysql. Ubuntu 24.04 ships PHP 8.3 by default, so the PPA provides the newer 8.4 packages.

Nginx has no built-in PHP interpreter, unlike Apache with mod_php. It forwards dynamic requests to PHP-FPM, a separate FastCGI Process Manager that runs the PHP code in its own worker pool. This keeps Nginx lean and lets PHP scale independently.

A server block is Nginx's configuration for a single site, equivalent to a virtual host in Apache. It defines the domain, document root, and how requests are handled, including which location block passes PHP files to PHP-FPM over the FastCGI socket.

fastcgi_pass tells Nginx where to send PHP requests. In a LEMP stack it points at the PHP-FPM Unix socket, such as unix:/run/php/php8.4-fpm.sock. If the socket path is wrong, Nginx cannot reach PHP and browsers download the .php file instead of running it.

For PHP 8.4 it is /run/php/php8.4-fpm.sock. The version number is part of the path, so if you install a different PHP version the socket changes accordingly. Always match the fastcgi_pass line in your server block to the version you installed.

It is a script bundled with MySQL that hardens a fresh install interactively. It can enforce a password policy, remove anonymous users, disable remote root login, drop the test database, and reload privilege tables. Run it once immediately after installing MySQL 8.

No. Create a dedicated user scoped to a single database and bound to localhost, then grant it privileges only on that database. If the application credential leaks, the damage is limited to one database and cannot be used to connect remotely.

Create a temporary info.php file containing phpinfo() in your web root and load it in a browser. If the PHP information page appears, Nginx and PHP-FPM are wired correctly. Delete the file straight after, because it exposes sensitive version and configuration details.

That means Nginx is not passing the request to PHP-FPM. Usually the location ~ \.php$ block is missing or the fastcgi_pass socket path is wrong. Check the socket matches your PHP version, run sudo nginx -t, then reload Nginx.

No. In a single-server LEMP stack, MySQL listens on localhost and only PHP-FPM on the same machine connects to it. Keep port 3306 closed to the internet with UFW and only open the Nginx HTTP and HTTPS ports.

A Unix socket is a file used for communication between processes on the same host and is slightly faster with less overhead. A TCP port like 127.0.0.1:9000 is needed when Nginx and PHP-FPM run on different machines. For a single server, the Unix socket is the default and preferred choice.

After the stack serves PHP over HTTP, install Certbot and request a free Let's Encrypt certificate. Certbot edits your Nginx server block to listen on port 443 and sets up automatic renewal, so your site is served over HTTPS with a trusted certificate.