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 sudo privileges.
  • 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.

Nginx default welcome page shown in a browser after installation on Ubuntu

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.

PHP info page displayed in a browser confirming PHP-FPM is working with Nginx

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