Getting PHP 8.5 running on RHEL-based systems takes a different path than Debian/Ubuntu. The AppStream repos ship older PHP versions, and the module stream system adds a step most admins forget. The Remi repository is your best bet here – it’s been the go-to PHP source for RHEL/CentOS since the PHP 5.x days, and the packages are built with the same spec files Red Hat uses internally.

This guide covers installing PHP 8.5 on Rocky Linux 10, AlmaLinux 10, and RHEL 10 using the Remi repository, setting up PHP-FPM with Nginx, production tuning, and handling the SELinux booleans that catch people off guard on RHEL systems.

Prerequisites

  • Rocky Linux 10, AlmaLinux 10, or RHEL 10 with sudo access
  • EPEL repository (we’ll install it below)
  • At least 512 MB RAM

Step 1: Install EPEL and Remi Repository

EPEL provides dependencies that Remi packages need. Install both:

sudo dnf install -y epel-release
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-10.rpm

For RHEL 10 specifically, enable CodeReady Builder (CRB) first since EPEL depends on it:

sudo subscription-manager repos --enable codeready-builder-for-rhel-10-x86_64-rpms
sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-10.rpm

Step 2: Reset PHP Module and Enable 8.5

RHEL 10 uses DNF module streams to manage PHP versions. You need to reset any existing PHP module and enable the Remi 8.5 stream:

sudo dnf module reset php -y
sudo dnf module enable php:remi-8.5 -y

Verify the stream is active:

$ dnf module list php
Name  Stream      Profiles               Summary
php   remi-8.5 [e] common [d], devel, minimal  PHP scripting language

The [e] flag confirms the remi-8.5 stream is enabled.

Step 3: Install PHP 8.5 with Extensions

sudo dnf install -y php php-fpm php-cli php-common \
  php-mysqlnd php-pgsql php-curl php-gd php-mbstring \
  php-xml php-zip php-intl php-redis php-opcache \
  php-bcmath php-soap php-ldap php-imagick

On RHEL-based systems, php-opcache is a separate package (unlike Debian where it’s bundled in common). The php-mysqlnd package is the native MySQL driver – use it instead of php-mysql.

Step 4: Verify the Installation

$ php --version
PHP 8.5.3 (cli) (built: Feb 13 2026 16:01:19) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.5.3, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.3, Copyright (c), by Zend Technologies
$ php -m | grep -c '^\w'
60

Step 5: Enable and Start PHP-FPM

sudo systemctl enable --now php-fpm

Verify:

$ sudo systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
     Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; enabled)
     Active: active (running)
   Main PID: 2345 (php-fpm)

Important RHEL difference: The FPM service is called php-fpm (not php8.5-fpm like on Debian). The socket path is also different:

$ ls -la /run/php-fpm/www.sock
srw-rw---- 1 apache apache 0 Mar 18 20:10 /run/php-fpm/www.sock

Note the socket is owned by apache (not www-data). This matters for Nginx integration.

Step 6: Configure php.ini

On RHEL, php.ini lives at /etc/php.ini (shared between CLI and FPM, unlike Debian):

sudo vim /etc/php.ini
memory_limit = 256M
max_execution_time = 60
upload_max_filesize = 64M
post_max_size = 64M
date.timezone = UTC

; OPcache
opcache.enable = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0

; JIT
opcache.jit = tracing
opcache.jit_buffer_size = 64M
sudo systemctl restart php-fpm

Step 7: Configure PHP-FPM for Nginx

By default, PHP-FPM on RHEL runs as the apache user and listens on a socket. For Nginx, you need to change the user or add Nginx to the Apache group. The cleaner approach is to update the FPM pool:

sudo vim /etc/php-fpm.d/www.conf
; Change user and group to nginx
user = nginx
group = nginx

; Socket permissions
listen = /run/php-fpm/www.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

; Pool tuning for 2GB server
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 500
sudo systemctl restart php-fpm

Step 8: Install and Configure Nginx

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

Create a PHP-enabled server block:

sudo tee /etc/nginx/conf.d/default.conf > /dev/null << 'NGINX'
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.php index.html;

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

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

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

sudo nginx -t && sudo systemctl restart nginx

Step 9: Open Firewall Ports

RHEL uses firewalld by default:

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

Step 10: Handle SELinux

This is where most guides leave you stranded. SELinux is enforcing by default on RHEL, Rocky, and AlmaLinux. If you skip this, PHP-FPM will silently fail to connect to databases, send emails, or write to directories outside the web root.

Enable the common SELinux booleans PHP applications need:

# Allow PHP-FPM to connect to databases (MySQL, PostgreSQL, Redis)
sudo setsebool -P httpd_can_network_connect_db 1

# Allow PHP to make outbound network connections (API calls, SMTP)
sudo setsebool -P httpd_can_network_connect 1

# Allow PHP to send emails
sudo setsebool -P httpd_can_sendmail 1

# Allow PHP to write to content directories (uploads, cache)
sudo setsebool -P httpd_unified 1

If your app writes to a custom directory outside /usr/share/nginx/html, set the SELinux context:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/myapp/storage(/.*)?"
sudo restorecon -Rv /var/www/myapp/storage

Step 11: Test

echo '<?php phpinfo(); ?>' | sudo tee /usr/share/nginx/html/info.php
curl -s http://localhost/info.php | head -5

Open http://your-server-ip/info.php in your browser to confirm PHP 8.5 is running through Nginx. Remove it after testing:

sudo rm /usr/share/nginx/html/info.php

Switch PHP Versions with DNF Module

To switch from PHP 8.5 to another version later:

# Reset current module
sudo dnf module reset php -y

# Switch to a different version
sudo dnf module enable php:remi-8.4 -y

# Reinstall (distro-sync ensures all packages match the new stream)
sudo dnf distro-sync -y

# Restart FPM
sudo systemctl restart php-fpm

RHEL vs Debian - Key PHP Differences

DetailRHEL/Rocky/AlmaDebian/Ubuntu
Package managerdnf + module streamsapt + PPA
FPM service namephp-fpmphp8.5-fpm
Socket path/run/php-fpm/www.sock/run/php/php8.5-fpm.sock
Default FPM userapachewww-data
php.ini location/etc/php.ini (shared)/etc/php/8.5/fpm/php.ini
FPM pool config/etc/php-fpm.d/www.conf/etc/php/8.5/fpm/pool.d/www.conf
SELinuxEnforcing (needs booleans)Not installed
Firewallfirewall-cmdufw or nftables
Web doc root/usr/share/nginx/html/var/www/html

Troubleshooting

DNF module conflict when installing:

If you see "Nothing to do" or "module stream conflict", reset the module first:

sudo dnf module reset php -y
sudo dnf module enable php:remi-8.5 -y
sudo dnf distro-sync -y

PHP-FPM starts but Nginx returns 502:

Check that the socket owner matches Nginx. On RHEL the default is apache but Nginx runs as nginx. Either change the FPM pool user to nginx (Step 7) or add the nginx user to the apache group:

sudo usermod -aG apache nginx
sudo systemctl restart nginx

PHP can't connect to MySQL/PostgreSQL:

SELinux is blocking it. Check the audit log and set the boolean:

sudo ausearch -m avc -ts recent | grep php
sudo setsebool -P httpd_can_network_connect_db 1

OPcache not enabled:

Make sure the php-opcache package is installed (it's separate on RHEL):

sudo dnf install -y php-opcache
sudo systemctl restart php-fpm

Conclusion

PHP 8.5 on RHEL-based systems works well once you handle the three things that differ from Debian: DNF module streams for version management, the FPM socket user/path for Nginx, and SELinux booleans for database and network access. Skip any of those and you'll spend an hour debugging silent failures. Get them right upfront and everything runs smoothly.

Related guides: