LEMP is the stack of Linux, Nginx (pronounced "engine-x", hence the E), MySQL, and PHP. Unlike LAMP with Apache, Nginx serves static files and proxies dynamic requests to PHP-FPM over a separate socket — that means lower memory use under many concurrent connections, and it requires explicitly configuring the bridge between Nginx and PHP.
Installing LEMP on Ubuntu 22.04
The order only matters formally — the components are independent, but it is convenient to install the web server first so you can immediately see that port 80 responds.
1. Installing Nginx
apt update
apt install nginx -y
systemctl enable nginx
systemctl start nginx
# Check: should return a Server: nginx header
curl -I http://localhost
2. Installing MySQL
apt install mysql-server -y
mysql_secure_installation
The mysql_secure_installation wizard sets a root password, disables anonymous users, blocks remote root login, and removes the test database — all four steps are worth confirming on a production server.
3. Installing PHP
apt install php8.2-fpm php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip -y
# Confirm the FPM daemon is running and listening on its socket
systemctl status php8.2-fpm
ls -l /run/php/php8.2-fpm.sock
Modules are chosen based on the task: php8.2-mysql is needed to connect to the database, php8.2-mbstring for correct UTF-8 handling in CMSs like WordPress, php8.2-gd for image processing. The exact module list for popular applications is usually listed in their documentation.
4. Configuring Nginx for PHP
Edit /etc/nginx/sites-available/default or create a separate config for the site:
server {
listen 80;
server_name yourdomain.com;
root /var/www/yourdomain.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}
The fastcgi_pass directive points to the PHP-FPM unix socket — the same path shown by the ls command in the previous step. If the socket has a different name (for example, a TCP port 127.0.0.1:9000 is set in the FPM pool), you need to specify that instead.
5. Restart Nginx
nginx -t && systemctl reload nginx
Checking the syntax with nginx -t before reload is not a formality: it tells apart a typo in the config from a real problem after a restart, when the site suddenly does not open.
Common Errors Explained
502 Bad Gateway — Nginx accepted the request but could not get a response from PHP-FPM. There are usually two causes: the socket in fastcgi_pass does not match the FPM socket's actual path, or the php8.2-fpm process itself is not running. Check with systemctl status php8.2-fpm and the log at /var/log/nginx/error.log, which will show a line like connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory).
Downloading a PHP file instead of executing it in the browser means the location ~ \.php$ block did not fire — usually because of a typo in the regular expression or a missing fastcgi_params include.
Comparing LEMP and LAMP
| Parameter | LEMP (Nginx) | LAMP (Apache) |
|---|---|---|
| Request handling model | Event-driven, one process for thousands of connections | Processes or threads per connection (mod_php), or via FPM |
| Memory under many connections | Lower at high concurrency | Higher with mod_php, comparable when paired with FPM |
| .htaccess | Not supported, rules move into the site's config | Supported out of the box |
| Learning curve | Steeper, needs explicit setup of the PHP bridge | Gentler, mod_php works right away |
When LEMP Does Not Fit
- The application or its hosting instructions depend heavily on
.htaccessfor overriding rules at the directory level — Nginx has no such mechanism, all rules are centralized in the site's config. - The server already runs a control panel that generates its own Apache configuration (for example, a familiar cPanel setup) — mixing two web servers on the same port without explicit proxying is not a good idea.
- The team is used to debugging through
.htaccessand has no access to reload Nginx (for example on shared hosting) — LEMP assumes control over the server config, not just over the site's files.
Verifying the Result
- Create a test file
info.phpwith the content<?php phpinfo();in the site root and open it in a browser — it should display the PHP configuration page, not its source code. - Delete
info.phpright after the check — this page reveals software versions and server paths, and must never stay in production. - Check the PHP-to-MySQL connection:
mysql -u root -p -e "SELECT VERSION();"and a matching query through a test PHP script usingmysqlior PDO. - Confirm all three services come back up automatically after a reboot:
systemctl is-enabled nginx mysql php8.2-fpmshould returnenabledfor each.
Further tuning of the virtual host, gzip, and static caching is covered in the article on configuring Nginx for a site on a VDS, and attaching a domain to TLS is covered in the article on installing SSL with Certbot.