Cloud & Server Infrastructure
Configuring a Production LEMP Stack (Linux, Nginx, MariaDB, PHP)
Last reviewed
Summary: A LEMP stack is Linux, Nginx (pronounced 'engine-x'), MariaDB and PHP. Install the three packages from Ubuntu's repositories, secure MariaDB, connect Nginx to PHP-FPM through a Unix socket, then size PHP-FPM's process pool to the RAM you have. Harden the server first with our checklist.
01
Browser
HTTPS request
02
Nginx
Serves static files directly
03
PHP-FPM
Runs .php via Unix socket
04
MariaDB
Queries over local socket
1. Install the packages
sudo apt update
sudo apt install -y nginx mariadb-server php-fpm php-mysql php-curl php-mbstring php-xml php-zip php-gd php-intl
php -v # note the version, e.g. 8.32. Secure MariaDB
sudo mariadb-secure-installation
sudo mariadb <<'SQL'
CREATE DATABASE app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'app'@'localhost' IDENTIFIED BY 'use-a-long-random-password';
GRANT ALL PRIVILEGES ON app.* TO 'app'@'localhost';
FLUSH PRIVILEGES;
SQL3. Configure an Nginx server block
server {
listen 80;
listen [::]:80;
server_name example.ca www.example.ca;
root /var/www/example.ca/public;
index index.php index.html;
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 ~* \.(css|js|png|jpg|webp|svg|woff2)$ {
expires 30d;
access_log off;
}
location ~ /\. { deny all; }
}sudo ln -s /etc/nginx/sites-available/example.ca /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx4. Tune PHP-FPM workers
Each PHP-FPM child uses memory, often 40–80 MB for a CMS. Measure your real average, then set pm.max_children to roughly (RAM available for PHP ÷ average process size). Setting it too high causes swapping; too low causes queued requests and 502/504 errors.
# Average PHP-FPM process size in MB
ps -ylC php-fpm8.3 --sort:rss | awk 'NR>1 {sum+=$8; n++} END {print sum/n/1024 " MB"}'pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 5005. Tune Nginx workers
worker_processes auto;
events {
worker_connections 1024;
}
http {
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
client_max_body_size 64m;
}Finally, add HTTPS with our SSL/TLS guide. If you have not yet secured the server, start with the hardening checklist. More topics live in server infrastructure.
Get new technical guides by email
Occasional, practical server and hosting tutorials for Canadian webmasters. Unsubscribe any time.