Quick answer: Run sudo apt install nginx mariadb-server php-fpm php-mysql, secure MariaDB with mysql_secure_installation, then edit your Nginx server block so .php requests are passed to the PHP-FPM socket. Test with a phpinfo() file and delete it afterwards.
LEMP is Linux, Nginx (pronounced “engine-x”, hence the E), MariaDB and PHP. It is the usual alternative to a LAMP stack, and on a small VPS it tends to serve more concurrent visitors on less memory.
This guide covers the stack itself. If your goal is specifically to run WordPress, follow our WordPress on Nginx guide instead, which includes the database, permissions and SSL steps WordPress needs.
Updated for modern Ubuntu. The original version of this article installed PHP 5, which lost security support at the end of 2018. Everything below uses currently supported packages.
Before you start
- A server running Ubuntu 24.04 or 22.04 LTS.
- A user account with
sudoprivileges. - SSH access.
sudo apt update && sudo apt upgrade -y
Step 1: Install Nginx
sudo apt install nginx -y
sudo systemctl enable --now nginx
If UFW is active, open the web ports:
sudo ufw allow 'Nginx Full'
Browse to your server’s IP address. The default Nginx page means the web server is running.
Step 2: Install MariaDB
sudo apt install mariadb-server -y
sudo systemctl enable --now mariadb
Then run the security script:
sudo mysql_secure_installation
| Prompt | Answer | Reason |
|---|---|---|
| Enter current password for root | Press Enter | No password is set on a fresh install |
| Switch to unix_socket authentication | Y | Ties root access to the system account, which is stronger than a password |
| Change the root password | N | Unnecessary once socket auth is enabled |
| Remove anonymous users | Y | They permit unauthenticated connections |
| Disallow root login remotely | Y | Root has no business connecting over the network |
| Remove test database | Y | Accessible to everyone by default |
| Reload privilege tables | Y | Applies the changes at once |
Check it is healthy:
sudo systemctl status mariadb
Step 3: Install PHP-FPM
Nginx cannot run PHP by itself. It hands PHP requests to a separate process manager, PHP-FPM, over a socket.
sudo apt install php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip -y
Ubuntu 24.04 installs PHP 8.3; Ubuntu 22.04 installs PHP 8.1. Check which you have, because the version appears in the socket path:
php -v
Step 4: Tell Nginx to handle PHP
This is the step people miss, and the reason PHP files download instead of running.
sudo nano /etc/nginx/sites-available/default
Adjust the file so it matches this, changing the PHP version in the socket path to yours:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm;
server_name _;
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;
}
}
Two lines do the real work: adding index.php to the index directive so PHP files are served as the default document, and the location ~ \.php$ block that forwards PHP to FPM.
Test the syntax before applying it, then reload:
sudo nginx -t
sudo systemctl reload nginx
The nginx -t check is worth the habit. A typo in a config file will stop Nginx from starting, and it is much easier to catch it while the running server is still serving.
Step 5: Verify PHP is working
Create a temporary test file:
sudo nano /var/www/html/test.php
Add:
<?php
phpinfo();
?>
Visit http://your-server-ip/test.php. You should see the PHP information page listing your version and loaded modules.
Delete this file as soon as you have checked it:
sudo rm /var/www/html/test.php
That page reveals your PHP version, installed extensions and paths, which is precisely the reconnaissance an attacker wants. Leaving it in place is a genuine, and very common, mistake.
Step 6: Create a database and user for your application
Whatever you host next will need its own database. Never point an application at the root account.
sudo mysql
CREATE DATABASE appdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a_strong_password';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
LEMP or LAMP?
| LEMP (Nginx) | LAMP (Apache) | |
|---|---|---|
| Memory use under load | Lower, event-driven | Higher, process or thread per connection |
| Static file serving | Very fast | Good |
| Per-directory config | None, all central | .htaccess supported |
| Learning curve | Steeper, config is explicit | Gentler, more forgiving |
| Shared hosting compatibility | Less common | The usual default |
| Best suited to | Small VPS, high traffic, static-heavy sites | Apps expecting .htaccess, mixed hosting |
The absence of .htaccess is the trade-off that catches people out. Many application install guides assume it exists, so with Nginx you translate those rules into the server block instead. In exchange, you get a meaningful performance advantage on constrained hardware.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Browser downloads .php files | No PHP location block | Add the location ~ \.php$ block and reload Nginx |
| 502 Bad Gateway | Socket path does not match the PHP version | Run php -v, then correct fastcgi_pass |
| Nginx will not start | Config syntax error, or port 80 in use | Run sudo nginx -t; check whether Apache is already installed |
| 403 Forbidden | Permissions on the web root | sudo chown -R www-data:www-data /var/www/html |
| Cannot connect to MariaDB | Service stopped | sudo systemctl status mariadb, then start it |
| PHP changes have no effect | FPM still running old config | sudo systemctl restart php8.3-fpm |
Frequently asked questions
Can Apache and Nginx run on the same server?
They can, but not both on port 80. If Nginx refuses to start, Apache is usually already bound to it. Remove whichever you do not need.
Is MariaDB really the same as MySQL?
For nearly all applications, yes. It began as a fork and remains a drop-in replacement, and it is what Ubuntu packages by default.
How do I host multiple sites?
Create a separate file in /etc/nginx/sites-available for each site, each with its own server_name and root, then symlink them into sites-enabled.
Should I add SSL?
Yes, for anything public. Install Certbot with sudo apt install certbot python3-certbot-nginx and run sudo certbot --nginx.
Does this work on Debian?
Yes. The commands are identical; only the default PHP version differs.
Related guides
- How to Install WordPress with Nginx, PHP-FPM and MariaDB on Ubuntu, the full WordPress build on top of this stack.
- Install VirtualBox on a Headless Ubuntu Server, handy for practising this build in a disposable VM first.

