LEMP Field Notes
Stack Guides

How to Install a LEMP Stack on Ubuntu 24.04 (Step by Step)

How to Install a LEMP Stack on Ubuntu 24.04 (Step by Step)
In briefTo install a LEMP stack on Ubuntu 24.04, run sudo apt update, then install each component from the default repositories: sudo apt install nginx, sudo apt install mysql-server, and sudo apt install php8.3-fpm php8.3-mysql. Secure MySQL with mysql_secure_installation, then configure an Nginx server block that passes .php requests to PHP-FPM via the Unix socket /run/php/php8.3-fpm.sock. Test with sudo nginx -t, reload Nginx, and verify with a phpinfo() page — then delete that test file.

How to Install a LEMP Stack on Ubuntu 24.04

To install a LEMP stack on Ubuntu 24.04, you install Nginx, MySQL, and PHP-FPM from the default Ubuntu repositories with apt, then configure Nginx to pass PHP requests to PHP-FPM through a Unix socket. The whole process takes four commands to get the software on disk and one short config file to wire it together. Ubuntu 24.04 LTS (Noble Numbat) ships current, well-tested versions of all three components, so you don't need any third-party PPAs for a standard setup.

This guide walks through every step on a fresh Ubuntu 24.04 server: installing each component, securing MySQL, connecting Nginx to PHP-FPM, and verifying the stack end to end with a test PHP page. Every command is shown exactly as you should type it, and we explain what each one changes on your system.

What you need before starting:

If you're still deciding between web servers, our comparison of Nginx vs Apache covers the trade-offs — the short version is that Nginx's event-driven model and lighter memory footprint make it the usual choice for a new stack, which is exactly why the "E" in LEMP replaced the "A" in LAMP.

What Is a LEMP Stack?

A LEMP stack is a set of open-source software for serving dynamic websites: Linux (the operating system), Engine-X — Nginx (the web server), MySQL (the database), and PHP (the application language). Nginx accepts HTTP requests, hands PHP files to the PHP-FPM process manager for execution, and PHP talks to MySQL for data. It's the stack behind a huge share of WordPress, Laravel, and custom PHP deployments.

On Ubuntu 24.04, the default repositories provide:

Component Package Version in Ubuntu 24.04 repos
Web server nginx 1.24.x
Database mysql-server MySQL 8.0.x
Language php8.3-fpm PHP 8.3

These versions are maintained with security patches by Ubuntu for the life of the LTS release, which is why we recommend the default repos over third-party sources unless you specifically need a newer PHP.

Step 1 — Update the System and Install Nginx

Start by refreshing the package index and applying any pending updates:

sudo apt update
sudo apt upgrade -y

Then install Nginx:

sudo apt install nginx -y

Ubuntu starts and enables the Nginx service automatically on install. Confirm it's running:

systemctl status nginx

You should see active (running). Press q to exit the status view.

Open the Firewall

Ubuntu 24.04 ships with ufw available (and on many cloud images, enabled). Allow HTTP traffic — and SSH, so you don't lock yourself out:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx HTTP'
sudo ufw enable

If ufw was already enabled, skip the last command. The 'Nginx HTTP' profile opens port 80 only; you'll open 443 later when you add HTTPS with Let's Encrypt.

Now visit your server's IP address in a browser (http://your_server_ip). You should see the default "Welcome to nginx!" page. If you don't know the IP, ip addr on the server or your provider's dashboard will show it.

Step 2 — Install MySQL

Install the MySQL server package:

sudo apt install mysql-server -y

The service starts automatically. Verify:

systemctl status mysql

Secure the MySQL Installation

MySQL ships with a helper script that tightens the default configuration:

sudo mysql_secure_installation

The script asks a series of yes/no questions:

One Ubuntu-specific detail worth knowing: on Ubuntu, the MySQL root account authenticates via the auth_socket plugin by default, meaning you log in with sudo mysql rather than a password. This is more secure for local administration than a password, and we recommend leaving it that way. If the secure-installation script prompts you to set a root password, you can do so, but the socket-based login continues to work for the root system user.

Create an Application Database and User

Don't let your applications connect as root. Log in and create a dedicated database and user (replace the names and choose a strong password):

sudo mysql
CREATE DATABASE example_db;
CREATE USER 'example_user'@'localhost' IDENTIFIED BY 'choose_a_strong_password';
GRANT ALL PRIVILEGES ON example_db.* TO 'example_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Test the new account:

mysql -u example_user -p

Enter the password when prompted, then EXIT; to leave.

Step 3 — Install PHP-FPM

With Apache, PHP typically runs inside the web server process. Nginx works differently: it doesn't execute PHP itself, so you install PHP-FPM (FastCGI Process Manager), a separate service that runs PHP and communicates with Nginx over a socket.

Install PHP-FPM plus the MySQL extension so PHP can talk to your database:

sudo apt install php8.3-fpm php8.3-mysql -y

On Ubuntu 24.04, php-fpm resolves to PHP 8.3; we name the version explicitly so the commands below (which reference the versioned socket path) match exactly. Check the service:

systemctl status php8.3-fpm

Most real applications need a few more extensions. Common ones for WordPress, Laravel, and similar apps:

sudo apt install php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip -y

You can always add extensions later; after installing one, restart PHP-FPM with sudo systemctl restart php8.3-fpm.

Step 4 — Configure Nginx to Use PHP-FPM

This is the step that turns three separate services into a stack. You'll create an Nginx server block — the Nginx equivalent of an Apache virtual host — that serves files from a directory and passes anything ending in .php to PHP-FPM.

First, create a web root for your site and give your user ownership:

sudo mkdir -p /var/www/example
sudo chown -R $USER:$USER /var/www/example

Now create the server block file:

sudo nano /etc/nginx/sites-available/example

Paste this configuration, replacing example.com with your domain (or your server's IP if you don't have a domain yet):

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example;

    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;
    }

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

What each part does:

Enable the site by symlinking it into sites-enabled, and remove the default site so it doesn't conflict:

sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default

Always test before reloading. This habit will save you at some point:

sudo nginx -t

If you see syntax is ok and test is successful, apply the change:

sudo systemctl reload nginx

If the test fails, it prints the file and line number of the problem — fix it and test again. Nginx keeps running with the old config until a reload succeeds, so a failed test never takes your site down.

This single-site setup is all you need today, but the same mechanism scales to many sites on one server — see our full guide to Nginx server blocks for hosting multiple domains, choosing server_name values, and how sites-available and sites-enabled relate.

Step 5 — Test the Stack End to End

Create a PHP file in your web root:

nano /var/www/example/info.php

Add:

<?php
phpinfo();

Visit http://your_server_ip/info.php (or your domain). You should see the PHP information page showing PHP 8.3, with the Server API reported as FPM/FastCGI. That confirms the full chain: Nginx received the request, passed it to PHP-FPM, and PHP executed it.

Test the Database Connection

To confirm PHP can reach MySQL, replace the contents of info.php with a quick connection test using the user you created in Step 2:

<?php
$mysqli = new mysqli("localhost", "example_user", "choose_a_strong_password", "example_db");
if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}
echo "Connected to MySQL successfully.";

Reload the page. "Connected to MySQL successfully." means all three layers are talking.

Now delete the test file. A phpinfo() page exposes detailed server configuration to anyone who finds it, and a file containing database credentials should never sit in a web root:

rm /var/www/example/info.php

Troubleshooting Common Issues

Next Steps

You have a working LEMP stack, but a production server needs two more things before it hosts anything real:

  1. HTTPS. Serving over plain HTTP is no longer acceptable for anything with logins or forms. Follow our Let's Encrypt with Certbot tutorial to get a free, auto-renewing TLS certificate — it takes about ten minutes on the stack you just built, and Certbot will edit this same server block for you.
  2. Deploy an application. Point the root directive at your application's public directory, install any PHP extensions it requires, and give it the database credentials from Step 2.

The stack you've built — Nginx 1.24, MySQL 8.0, PHP 8.3 on Ubuntu 24.04 LTS — is supported with security updates through the standard LTS window, so it's a foundation you can leave in place for years. Keep it patched with a periodic sudo apt update && sudo apt upgrade, and test config changes with nginx -t before every reload. Those two habits prevent most of the outages that beginner-run servers suffer.

FAQ

What versions of Nginx, MySQL, and PHP does Ubuntu 24.04 include?

Ubuntu 24.04 LTS ships Nginx 1.24.x, MySQL 8.0.x, and PHP 8.3 in its default repositories. These versions receive security patches from Ubuntu for the life of the LTS release, so for a standard LEMP setup you don't need third-party PPAs. Only add an external repository if your application specifically requires a PHP version newer than 8.3.

What is the difference between LEMP and LAMP?

The only difference is the web server: LAMP uses Apache, while LEMP uses Nginx (pronounced "Engine-X," hence the E). Both stacks run on Linux with MySQL and PHP. Because Nginx doesn't execute PHP internally the way Apache commonly does, a LEMP stack runs PHP through PHP-FPM, a separate process manager that Nginx communicates with over a Unix socket.

Why doesn't MySQL ask for a root password on Ubuntu?

On Ubuntu, MySQL's root account uses the auth_socket authentication plugin by default. That means you log in with sudo mysql as the system root/sudo user rather than typing a database password. This is considered more secure for local administration and is safe to leave in place. For applications, create a dedicated MySQL user with a password and grant it privileges on only its own database.

How do I fix a 502 Bad Gateway error on a LEMP stack?

A 502 means Nginx couldn't reach PHP-FPM. First check the service is running with systemctl status php8.3-fpm. Then confirm the fastcgi_pass line in your server block points to the correct socket — on Ubuntu 24.04 that's unix:/run/php/php8.3-fpm.sock. A socket path written for a different PHP version is the most common cause. After fixing it, run sudo nginx -t and reload Nginx.

Do I need to install phpMyAdmin with a LEMP stack?

No — it's optional. You can fully administer MySQL from the command line with sudo mysql, which is what this guide uses. phpMyAdmin adds a convenient web interface but also expands your attack surface, since it's a frequently scanned target. If you install it, restrict access (for example by IP allowlist or HTTP authentication) and always serve it over HTTPS.

How do I add HTTPS after installing the LEMP stack?

Use Certbot with Let's Encrypt. Once your domain's DNS points at the server and your Nginx server block's server_name matches the domain, Certbot can obtain a free certificate, update the server block for you, and set up automatic renewal. You'll also need to open port 443 in your firewall with sudo ufw allow 'Nginx Full'. The process takes about ten minutes on a working stack.