How to Install Nginx, PHP-FPM and MariaDB (LEMP) on Ubuntu

A complete LEMP setup on Ubuntu 24.04 — Nginx, PHP 8.3-FPM, MariaDB — with a working server block, a secure database, sensible PHP settings and HTTPS.

Published
Reading time
3 min

LEMP — Linux, Nginx ("engine-x"), MariaDB/MySQL, PHP — is the stack behind most PHP hosting. This is a clean install on Ubuntu 24.04 that ends with a site serving PHP over Nginx with a database ready. About twenty minutes, assuming a server that has been through the hardening checklist.

1. Nginx

bash
sudo apt update
sudo apt install nginx -y
sudo systemctl enable --now nginx

Open http://your-server-ip in a browser: the Nginx welcome page confirms it is serving. If you use ufw:

bash
sudo ufw allow 'Nginx Full'

2. MariaDB

bash
sudo apt install mariadb-server -y
sudo systemctl enable --now mariadb
sudo mysql_secure_installation

Answer the prompts: set a root password (or keep unix socket auth — fine for a single server), remove anonymous users, disallow remote root, remove the test database, reload privileges. All yes.

Create a database and a user for the site:

bash
sudo mysql
sql
CREATE DATABASE example_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'example_user'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON example_db.* TO 'example_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

One user per site, with rights on one database only. Never let the application use root.

3. PHP-FPM

bash
sudo apt install php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring \
  php8.3-xml php8.3-zip php8.3-intl php8.3-bcmath php8.3-opcache -y
php -v

That extension list covers WordPress, Laravel and most PHP applications. PHP-FPM runs as a pool of worker processes listening on a Unix socket, /run/php/php8.3-fpm.sock; Nginx hands PHP requests to it.

Three settings in /etc/php/8.3/fpm/php.ini worth changing from the defaults:

ini
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M

And enable OPcache properly in the same file:

ini
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
bash
sudo systemctl restart php8.3-fpm

Tuning the pool sizes for real traffic is a separate topic: configuring PHP-FPM pools.

4. A server block for the site

Create the web root and a config:

bash
sudo mkdir -p /var/www/example.com
sudo chown -R www-data:www-data /var/www/example.com
sudo nano /etc/nginx/sites-available/example.com
nginx
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.php index.html;

    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log;

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

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

    location ~ /\.(?!well-known) {
        deny all;
    }

    location ~* \.(css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2?)$ {
        expires 30d;
        access_log off;
    }
}

The try_files line is what makes WordPress permalinks and Laravel routes work; the \. block stops .git and .env being served; the last block caches static files in the browser.

Enable it, remove the default, test and reload:

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

5. Test PHP

bash
echo '<?php phpinfo();' | sudo tee /var/www/example.com/info.php

Load http://example.com/info.php (or the IP, if DNS is not pointed yet — add server_name _; temporarily). A page of PHP configuration means the whole chain works. Delete the file afterwards; it reveals more than you want public:

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

6. HTTPS

With DNS pointed at the server:

bash
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com

Certbot edits the server block for you, adds the redirect, and installs a renewal timer. Check it with sudo certbot renew --dry-run.

What you have

Nginx serving static files and passing PHP to a FastCGI pool, a database with a least-privilege user, OPcache on, HTTPS with auto-renewal. That is a production stack; from here it is application deployment and tuning.

If you would rather not maintain it: this exact stack, tuned for the workload and patched by us, is what a VPSPioneer managed VPS ships with.

#nginx#php#mariadb#ubuntu#lemp#servers

Keep reading

More from Servers

All guides

Servers

Apache vs Nginx: Which Web Server Should You Run?

How Apache and Nginx differ in architecture, performance and .htaccess support, why many hosts run both, and which to choose for PHP, an API or static files.

3 min read →

Servers

Managed vs Unmanaged VPS: Which One Do You Actually Need?

What a host does on a managed VPS and what is left to you on an unmanaged one, the real time cost of self-management, and a short test for which to choose.

3 min read →

Servers

What Is a VPS? Shared Hosting vs VPS vs Dedicated Server

What a virtual private server is, how it differs from shared hosting and a dedicated machine, and how to tell which one your site actually needs.

3 min read →