How To

Install LEMP Stack on Ubuntu 26.04 LTS (Nginx, MariaDB, PHP 8.5)

Most production web stacks on Ubuntu run Nginx over Apache these days. Nginx handles static content and reverse proxying more efficiently, consumes less memory per connection, and scales better under high concurrency. Paired with MariaDB and PHP-FPM, you get a LEMP stack that powers everything from WordPress sites to custom PHP applications.

Original content from computingforgeeks.com - post 165988

This guide walks through a complete LEMP installation on Ubuntu 26.04 LTS (Resolute Raccoon) with Nginx 1.28.3, MariaDB 11.8.6, and PHP 8.5.4. Every command has been tested on a fresh install, and the article ends with production tuning tips for worker processes, PHP-FPM pools, and MariaDB buffer sizing.

Verified working: April 2026 on Ubuntu 26.04 LTS (Resolute Raccoon), Nginx 1.28.3, MariaDB 11.8.6, PHP 8.5.4

Prerequisites

Before starting, make sure you have:

  • Ubuntu 26.04 LTS server with root or sudo access (initial server setup guide)
  • At least 1 GB RAM and 10 GB disk space
  • SSH access to the server
  • Tested on: Ubuntu 26.04 LTS (kernel 7.0.0), Nginx 1.28.3, MariaDB 11.8.6, PHP 8.5.4

Update System Packages

Start with a package index refresh to make sure you pull the latest versions from the Ubuntu repositories.

sudo apt update && sudo apt upgrade -y

On a fresh 26.04 install, expect around 100 packages to upgrade. This takes a minute or two depending on your connection speed.

Install Nginx

Nginx is available directly from the Ubuntu 26.04 default repositories. The version shipped with Resolute Raccoon is 1.28.3, which includes HTTP/3 support and improved QUIC handling.

sudo apt install -y nginx

Verify the installed version:

nginx -v

You should see the version confirmed:

nginx version: nginx/1.28.3 (Ubuntu)

Check that the service is running:

systemctl status nginx

The output should show active (running):

● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-04-14 00:24:58 UTC; 9s ago
       Docs: man:nginx(8)
    Process: 2537 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 2538 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 2566 (nginx)
      Tasks: 3 (limit: 3522)

Nginx is enabled by default on Ubuntu 26.04, so it will start automatically on reboot.

Install MariaDB

Ubuntu 26.04 ships MariaDB 11.8.6, a significant jump from the 10.x series in older Ubuntu releases. The 11.x branch brings improved optimizer performance, better JSON handling, and native vector search support.

sudo apt install -y mariadb-server mariadb-client

Confirm the version:

mariadb --version

Expected output:

mariadb from 11.8.6-MariaDB, client 15.2 for debian-linux-gnu (x86_64) using  EditLine wrapper

The service starts automatically after installation. Verify it:

systemctl status mariadb

Look for active (running) and the status message “Taking your SQL requests now…”:

● mariadb.service - MariaDB 11.8.6 database server
     Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-04-14 00:25:25 UTC; 11s ago
       Docs: man:mariadbd(8)
             https://mariadb.com/kb/en/library/systemd/
   Main PID: 3396 (mariadbd)
     Status: "Taking your SQL requests now..."
      Tasks: 14 (limit: 23249)

Secure the MariaDB Installation

Set a root password for MariaDB. On Ubuntu 26.04, MariaDB uses unix_socket authentication by default, which means root can connect without a password when logged in as the system root user. For applications that connect with a password, set one explicitly:

sudo mariadb

Inside the MariaDB shell, set the root password:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPassword';
FLUSH PRIVILEGES;
EXIT;

Test the new password by logging in with it:

mariadb -u root -p

Enter your password at the prompt. If you get the MariaDB shell, the password is working correctly.

Install PHP 8.5 with PHP-FPM

Ubuntu 26.04 includes PHP 8.5.4, which brings property hooks, asymmetric visibility, and the new Pipe operator. Install PHP-FPM along with the most commonly needed extensions:

sudo apt install -y php-fpm php-mysql php-cli php-curl php-gd php-mbstring php-xml php-zip

This pulls in php8.5-fpm and all the listed extensions. Verify the PHP version:

php --version

Output:

PHP 8.5.4 (cli) (built: Apr  1 2026 09:36:11) (NTS)
Copyright (c) The PHP Group
Built by Ubuntu
Zend Engine v4.5.4, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.4, Copyright (c), by Zend Technologies

Confirm PHP-FPM is running:

systemctl status php8.5-fpm

The status should show “Ready to handle connections”:

● php8.5-fpm.service - The PHP 8.5 FastCGI Process Manager
     Loaded: loaded (/usr/lib/systemd/system/php8.5-fpm.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-04-14 00:26:22 UTC; 6s ago
       Docs: man:php-fpm8.5(8)
   Main PID: 13719 (php-fpm8.5)
     Status: "Ready to handle connections"
      Tasks: 3 (limit: 3522)

Check that the PHP-FPM socket exists. Nginx communicates with PHP-FPM through this Unix socket rather than TCP, which reduces overhead:

ls -la /run/php/php8.5-fpm.sock

You should see the socket file owned by www-data:

srw-rw---- 1 www-data www-data 0 Apr 14 00:26 /run/php/php8.5-fpm.sock

List the enabled PHP modules to confirm everything installed correctly:

php -m | grep -iE 'mysql|curl|gd|mbstring|xml|zip'

All requested extensions should appear:

curl
gd
libxml
mbstring
mysqli
mysqlnd
pdo_mysql
SimpleXML
xml
xmlreader
xmlwriter
zip

Configure Nginx to Process PHP Files

By default, Nginx serves static files but does not know how to handle PHP. You need to edit the default server block to pass .php requests to PHP-FPM via the Unix socket.

Open the default site configuration:

sudo vi /etc/nginx/sites-available/default

Replace the entire contents with:

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.5-fpm.sock;
    }

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

The key line is fastcgi_pass unix:/run/php/php8.5-fpm.sock, which tells Nginx to forward PHP requests to the FPM process manager over a Unix socket. The snippets/fastcgi-php.conf include handles the FastCGI parameter mapping.

Test the configuration for syntax errors:

sudo nginx -t

A clean test returns:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload Nginx to apply the changes:

sudo systemctl reload nginx

Configure the Firewall

If UFW is installed (it is by default on Ubuntu 26.04), allow HTTP and HTTPS traffic. Nginx registers application profiles with UFW during installation.

sudo ufw allow 'Nginx Full'
sudo ufw allow OpenSSH
sudo ufw enable

Verify the firewall rules:

sudo ufw status

The output confirms both Nginx and SSH are allowed:

Status: active

To                         Action      From
--                         ------      ----
Nginx Full                 ALLOW       Anywhere
OpenSSH                    ALLOW       Anywhere
Nginx Full (v6)            ALLOW       Anywhere (v6)
OpenSSH (v6)               ALLOW       Anywhere (v6)

Test PHP Processing

Create a PHP info page to confirm Nginx passes requests to PHP-FPM correctly:

echo '<?php phpinfo(); ?>' | sudo tee /var/www/html/info.php

Open http://10.0.1.50/info.php in your browser. You should see the full PHP information page showing PHP 8.5.4 with all the extensions you installed. The Server API line should read FPM/FastCGI, confirming that Nginx is routing through PHP-FPM rather than CGI.

Remove the info page after testing, since it exposes sensitive server details:

sudo rm /var/www/html/info.php

Create a Test Database

With all three components running, test the full stack by creating a database, populating it with sample data, and querying it from PHP. Log into MariaDB:

sudo mariadb

Create a database, a dedicated user, and a table with sample records:

CREATE DATABASE lemp_test;
CREATE USER 'lemp_user'@'localhost' IDENTIFIED BY 'YourStrongPassword';
GRANT ALL PRIVILEGES ON lemp_test.* TO 'lemp_user'@'localhost';
FLUSH PRIVILEGES;

USE lemp_test;
CREATE TABLE servers (
    id INT AUTO_INCREMENT PRIMARY KEY,
    hostname VARCHAR(100) NOT NULL,
    ip_address VARCHAR(45) NOT NULL,
    os VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO servers (hostname, ip_address, os) VALUES
    ('web01', '10.0.1.50', 'Ubuntu 26.04 LTS'),
    ('db01', '10.0.1.51', 'Ubuntu 26.04 LTS'),
    ('app01', '10.0.1.52', 'Rocky Linux 10');

SELECT * FROM servers;
EXIT;

The query returns the three sample records:

+----+----------+------------+-------------------+---------------------+
| id | hostname | ip_address | os                | created_at          |
+----+----------+------------+-------------------+---------------------+
|  1 | web01    | 10.0.1.50  | Ubuntu 26.04 LTS  | 2026-04-14 00:26:54 |
|  2 | db01     | 10.0.1.51  | Ubuntu 26.04 LTS  | 2026-04-14 00:26:54 |
|  3 | app01    | 10.0.1.52  | Rocky Linux 10    | 2026-04-14 00:26:54 |
+----+----------+------------+-------------------+---------------------+

Test PHP and MariaDB Together

Create a PHP script that connects to MariaDB and queries the test table. This validates the full LEMP chain: Nginx receives the request, passes it to PHP-FPM, and PHP queries MariaDB.

sudo vi /var/www/html/db_test.php

Add the following PHP code:

<?php
$host = "localhost";
$user = "lemp_user";
$pass = "YourStrongPassword";
$db   = "lemp_test";

$conn = new mysqli($host, $user, $pass, $db);
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "<h2>LEMP Stack Database Test</h2>";
echo "<p>Connected to MariaDB successfully.</p>";

$result = $conn->query("SELECT * FROM servers");
echo "<table border='1'><tr><th>ID</th><th>Hostname</th><th>IP</th><th>OS</th><th>Created</th></tr>";
while ($row = $result->fetch_assoc()) {
    echo "<tr><td>{$row['id']}</td><td>{$row['hostname']}</td><td>{$row['ip_address']}</td><td>{$row['os']}</td><td>{$row['created_at']}</td></tr>";
}
echo "</table>";
$conn->close();
?>

Open http://10.0.1.50/db_test.php in a browser. The page should display “Connected to MariaDB successfully” along with the servers table data. Once verified, remove the test file:

sudo rm /var/www/html/db_test.php

All Services at a Glance

Confirm all three LEMP components are active and enabled to start on boot:

systemctl is-active nginx mariadb php8.5-fpm

All three should return active:

active
active
active

Check that they are enabled for boot:

systemctl is-enabled nginx mariadb php8.5-fpm

Each should return enabled.

Nginx 1.28.3 MariaDB 11.8.6 PHP 8.5.4 active on Ubuntu 26.04 LTS
LEMP stack versions and service status on Ubuntu 26.04 LTS

Performance Tuning for Production

The default settings work for testing, but production workloads need tuning. Here are the key parameters for each component.

Nginx Worker Tuning

The default worker_processes auto in /etc/nginx/nginx.conf matches the number of CPU cores, which is correct for most cases. On a 2-core server, Nginx spawns 2 workers. The worker_connections directive defaults to 768, which limits each worker to 768 simultaneous connections.

For a server handling more traffic, increase worker_connections in the events block:

sudo vi /etc/nginx/nginx.conf

Find the events block and adjust:

events {
    worker_connections 2048;
    multi_accept on;
}

With multi_accept on, each worker accepts all new connections at once instead of one at a time. On a 2-core server with 2048 connections per worker, Nginx can handle roughly 4096 concurrent connections.

PHP-FPM Pool Sizing

The default PHP-FPM pool at /etc/php/8.5/fpm/pool.d/www.conf uses pm = dynamic with pm.max_children = 5. For a server with 4 GB RAM, a rough formula for max_children is:

max_children = (Total RAM - System overhead) / Average PHP process size

Each PHP-FPM worker typically uses 30 to 50 MB. On a 4 GB server with ~1 GB reserved for the OS, MariaDB, and Nginx, that leaves about 3 GB for PHP. At 40 MB per process, you can set pm.max_children = 75. Adjust the pool config:

sudo vi /etc/php/8.5/fpm/pool.d/www.conf

Update the process manager settings:

pm = dynamic
pm.max_children = 75
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500

The pm.max_requests = 500 setting recycles workers after 500 requests, which prevents memory leaks from accumulating. Restart PHP-FPM after changes:

sudo systemctl restart php8.5-fpm

MariaDB Buffer Pool

The InnoDB buffer pool is the single most impactful MariaDB setting. It caches table data and indexes in memory, reducing disk I/O. The default is 128 MB, which is too small for anything beyond trivial databases.

Set it to about 50 to 70 percent of available RAM dedicated to MariaDB. On a 4 GB server where MariaDB is the primary consumer, 1 to 2 GB is reasonable:

sudo vi /etc/mysql/mariadb.conf.d/50-server.cnf

Under the [mariadbd] section, add or modify:

innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2

Setting innodb_flush_log_at_trx_commit = 2 gives a significant write performance boost at the cost of losing up to one second of transactions in a crash. For most web applications, this tradeoff is acceptable. Restart MariaDB to apply:

sudo systemctl restart mariadb

You can verify the buffer pool size took effect with:

mariadb -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"

Where to Go from Here

With LEMP running, you can deploy PHP applications like WordPress, Laravel, or custom frameworks. For container-based workflows, see our Docker CE installation guide for Ubuntu 26.04. If you need SSL with Let’s Encrypt on Nginx, that is covered separately. For PostgreSQL as an alternative to MariaDB, see our PostgreSQL 18 guide. If you prefer the Apache-based LAMP stack instead, we have a LAMP stack guide for Ubuntu 26.04 as well.

Related Articles

Containers Setup CapRover Self-Hosted PaaS on Ubuntu 24.04 AlmaLinux Install LEMP Stack on Rocky Linux 8|AlmaLinux 8 Debian Install Node.js 12 on Ubuntu / Debian / Linux Mint Databases How To Install MongoDB 7.0 on Ubuntu 22.04|20.04

Leave a Comment

Press ESC to close