How to Install a LEMP Stack on RHEL, Rocky, AlmaLinux and CentOS Stream

This guide shows how to install a reliable LEMP stack — Nginx, MariaDB (or MySQL-compatible server) and PHP-FPM — on RHEL-based systems such as RHEL, Rocky Linux, AlmaLinux and CentOS Stream. You’ll learn which repositories to enable, how to configure PHP-FPM to work with Nginx, basic SELinux and firewall adjustments, and simple checks to confirm everything runs correctly.

How to Install a LEMP Stack on RHEL, Rocky, AlmaLinux and CentOS Stream Thumbnail

Where this applies and what to prepare

These instructions target modern RHEL-family systems (RHEL 8/9, Rocky, Alma, CentOS Stream). Details like PHP module streams and package names can vary by distribution version. Before you begin, have:

  • root or sudo access to the server
  • network connectivity for package repositories
  • a basic firewall policy (firewalld) — if you use a different firewall, adapt the commands

High-level steps

  • Update the system and enable common repos (EPEL).
  • Install and start Nginx.
  • Install and secure MariaDB.
  • Install PHP-FPM and necessary extensions, configure pool for Nginx.
  • Create an Nginx site configuration to serve PHP via the PHP-FPM socket (or TCP), adjust SELinux and firewall, then verify.

1. Update system and enable EPEL

Keep the system current and install EPEL, which provides extra packages that can be useful.

sudo dnf update -y
sudo dnf install -y epel-release

On RHEL itself you might need a subscription or to enable appropriate repos; on Rocky/Alma/CentOS Stream the epel-release package usually works directly.

2. Install and start Nginx

Install Nginx from the default repositories and enable it to start on boot.

sudo dnf install -y nginx
sudo systemctl enable --now nginx

Quick check:

sudo systemctl status nginx
curl -I http://localhost

3. Install and secure MariaDB

MariaDB is commonly available as mariadb-server. After installation, run the interactive secure script to set a root password and remove test databases.

sudo dnf install -y mariadb-server
sudo systemctl enable --now mariadb
sudo mysql_secure_installation

The mysql_secure_installation script will prompt you to set a root password, remove anonymous users, disallow remote root login, and remove the test database. Answer according to your security needs.

4. Install PHP-FPM and extensions

On RHEL-family systems PHP is provided via AppStream module streams. First list available PHP module streams so you can choose the version you want:

dnf module list php

To install a chosen stream (example shows php:8.1 — pick the stream appropriate for your system), enable the module and install packages:

sudo dnf module enable php:8.1   # choose the stream you need
sudo dnf install -y php-fpm php-mysqlnd php-opcache php-xml php-gd php-mbstring

If you prefer newer PHP releases, the Remi repository is a common source, but enabling third-party repos is optional and outside this basic guide.

Enable and start php-fpm:

sudo systemctl enable --now php-fpm
sudo systemctl status php-fpm

Important PHP-FPM pool settings for Nginx

Edit the default pool file /etc/php-fpm.d/www.conf and ensure ownership and socket settings fit Nginx. Two common options:

  • Use a Unix socket (common and efficient): set listen = /run/php-fpm/www.sock, and make sure listen.owner and listen.group are nginx.
  • Use a TCP socket (e.g., 127.0.0.1:9000) if you prefer separation or remote PHP-FPM. If you use TCP, set the SELinux boolean to allow network connections.

Example important lines to check in /etc/php-fpm.d/www.conf:

user = nginx
group = nginx
listen = /run/php-fpm/www.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

After edits, restart PHP-FPM:

sudo systemctl restart php-fpm

5. Configure Nginx to serve PHP

Create a site directory and a simple phpinfo() test page:

sudo mkdir -p /usr/share/nginx/html/example.com
sudo chown -R nginx:nginx /usr/share/nginx/html/example.com
cat <<'EOF' | sudo tee /usr/share/nginx/html/example.com/index.php
<?php phpinfo();
EOF

Create an Nginx server block. Place it in /etc/nginx/conf.d/example.com.conf:

sudo tee /etc/nginx/conf.d/example.com.conf > /dev/null <<'NGINX'
server {
    listen 80;
    server_name example.com;

    root /usr/share/nginx/html/example.com;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php-fpm/www.sock;
    }

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

Test and reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

6. SELinux and firewall adjustments

If SELinux is enabled (check with sestatus), you may need to set file contexts and booleans.

  • Give the site directory the web content file context (persistent):
sudo dnf install -y policycoreutils-python-utils   # provides semanage if missing
sudo semanage fcontext -a -t httpd_sys_content_t '/usr/share/nginx/html/example.com(/.*)?'
sudo restorecon -Rv /usr/share/nginx/html/example.com

If the application needs writable directories (upload folders, cache), mark them with httpd_sys_rw_content_t instead:

sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/example.com/storage(/.*)?'
sudo restorecon -Rv /usr/share/nginx/html/example.com/storage

If you configured PHP-FPM to use TCP (e.g., 127.0.0.1:9000), allow Nginx to make network connections:

sudo setsebool -P httpd_can_network_connect on

Firewall (firewalld) — open HTTP and HTTPS:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

7. Verify everything works

Basic service status checks:

sudo systemctl status nginx
sudo systemctl status php-fpm
sudo systemctl status mariadb

Confirm ports are listening:

ss -ltnp | grep -E ':80|:443|:3306'
# or
curl -I http://localhost
curl http://localhost/index.php

Open http://your-server-ip/ or http://example.com/ in a browser. The index.php file should show the PHP information page if PHP-FPM and Nginx are connected correctly.

Notes and practical tips

  • If you need a newer PHP version than AppStream provides, consider enabling the Remi repository; follow Remi’s documentation for the appropriate distribution and version.
  • Using a Unix socket is usually faster and requires fewer SELinux booleans; using TCP is convenient if PHP-FPM runs on another host or container.
  • On RHEL systems with subscriptions, repository names and availability can differ — check your subscription manager and repository list if packages are missing.
  • For production, switch the test phpinfo() file out for your real application, enable HTTPS, and harden MariaDB credentials and network access.

Wrap-up

Following these steps you should have a working LEMP stack suitable for hosting PHP applications on RHEL-family systems. The key decisions are which PHP stream to use and whether to connect PHP-FPM via socket or TCP; both approaches are supported. After the basic setup, focus on hardening (HTTPS, database user privileges) and tuning (PHP-FPM pools, Nginx caching) for your workload.

Read more

Leave a Reply

Your email address will not be published. Required fields are marked *