| Tested on | Rocky Linux 10.2 (Red Quartz) |
|---|---|
| Package | php 8.3.31php 8.4.21php 8.3.31 |
| Applies to | RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora |
| Privilege | sudo or root |
| Scope | Upgrade PHP 8.3 to 8.4 on Rocky Linux, RHEL, AlmaLinux, and Oracle Linux, including pre-checks, migration steps, and post-upgrade verification. |
| Related guides | Add Linux To Windows Ad Domain Realm Boom Boot Linux Lvm Snapshot Rhel 8 Linux Boot In Single User Mode Rhel Centos 8 Linux Boot Rescue Mode Iso Rhel Centos 7 Boot With Old Kernel Version Rhel 8 Grubby |
If php -v reports PHP 8.3 on Enterprise Linux 10, the supported native path is package replacement—not installing PHP 8.4 beside the existing major release. Inventory installed php-* RPMs, check application compatibility, back up configuration, remove PHP 8.3, install matching php8.4-* packages, merge settings, and verify PHP-FPM and your web application.
This guide covers Rocky Linux, RHEL, AlmaLinux, Oracle Linux, and CentOS Stream when both versions use native AppStream packaging. For a host with no PHP installed, use install PHP 8.4 on Enterprise Linux instead.
dnf install php8.4 --allowerasing as the default upgrade shortcut, and do not use dnf remove 'php*' blindly—a broad glob can match unintended packages once PHP 8.4 RPMs are present.
php8.4-* packages are available in RHEL 10.2 and compatible Rocky Linux, AlmaLinux, and Oracle Linux releases. On CentOS Stream 10, confirm availability with dnf info php8.4 before you schedule the upgrade.
Prerequisites
- RHEL, Rocky Linux, AlmaLinux, or Oracle Linux 10.2+ with PHP 8.3 installed from unprefixed
php-*AppStream packages, or CentOS Stream 10 wherednf info php8.4confirms the target package set. - sudo command or root access.
- A maintenance window to stop PHP-FPM and the web server with systemctl command.
- Application, database, and configuration backups—and a tested rollback plan.
- A staging copy of the application when you can test PHP 8.4 compatibility before production.
Package operations in this guide use dnf command.
Check whether the system can upgrade to PHP 8.4
Confirm the operating system and that PHP 8.4 packages exist in repository metadata before you touch PHP 8.3.
Identify the distribution:
cat /etc/os-releaseSample output:
NAME="Rocky Linux"
VERSION="10.2 (Red Quartz)"
VERSION_ID="10.2"
PRETTY_NAME="Rocky Linux 10.2 (Red Quartz)"On RHEL, Rocky Linux, AlmaLinux, and Oracle Linux, VERSION_ID should be 10.2 or newer for the native php8.4-* set. On CentOS Stream 10, use package metadata rather than a downstream 10.2 point-release check.
Confirm the current PHP major version:
php -vSample output on PHP 8.3:
PHP 8.3.31 (cli) (built: May 5 2026 13:35:55) (NTS gcc x86_64)
Zend Engine v4.3.31, Copyright (c) Zend Technologies
with Zend OPcache v8.3.31, Copyright (c), by Zend TechnologiesExact patch and release strings vary by distribution update date. Confirm that the major version changes from PHP 8.3 to PHP 8.4 rather than requiring the same patch numbers shown here.
If php -v already reports PHP 8.4, you are past the package-replacement step—verify extensions and application behavior instead. If php is not installed, this guide does not apply—install PHP 8.4 on a fresh host first.
Check that PHP 8.4 is available in AppStream:
dnf info php8.4Sample output:
Available Packages
Name : php8.4
Version : 8.4.21
Release : 1.el10_2
Repository : appstream
Summary : PHP scripting language for creating dynamic web sitesIf php8.4 is missing, refresh enabled repository metadata with sudo dnf makecache --refresh, then run dnf info php8.4 again.
Identify how PHP is deployed on the host:
systemctl status php-fpmSample output when FPM is running:
● php-fpm.service - The PHP FastCGI Process Manager
Active: active (running)Check whether Apache or Nginx is in use:
systemctl is-active httpd nginxOn the tested host, httpd was active and nginx was inactive. Stop only the services that are actually running before package removal.
Inventory PHP 8.3 packages and extensions
Create the backup directory first, then save every package and extension list you need for rollback:
sudo mkdir -p /root/php83-upgradeList installed PHP packages through DNF and save the output:
dnf list --installed 'php*' | sudo tee /root/php83-upgrade/dnf-packages.txtSample output:
php.x86_64 8.3.31-1.el10_2
php-cli.x86_64 8.3.31-1.el10_2
php-fpm.x86_64 8.3.31-1.el10_2
php-mysqlnd.x86_64 8.3.31-1.el10_2
...List the same packages through rpm command and save the sorted list:
rpm -qa 'php*' | sort | sudo tee /root/php83-upgrade/rpm-packages.txtSample output:
php-8.3.31-1.el10_2.x86_64
php-cli-8.3.31-1.el10_2.x86_64
php-common-8.3.31-1.el10_2.x86_64
php-fpm-8.3.31-1.el10_2.x86_64
php-mysqlnd-8.3.31-1.el10_2.x86_64
...PHP 8.3 uses unprefixed names (php, php-fpm, php-mysqlnd). PHP 8.4 uses the php8.4-* prefix.
Save loaded extensions:
php -m | sudo tee /root/php83-upgrade/php83-modules.txtRecord the configuration files PHP actually loads:
php --iniSample output:
Configuration File (php.ini) Path: /etc
Loaded Configuration File: /etc/php.ini
Scan for additional .ini files in: /etc/php.dMap each installed PHP 8.3 package to its PHP 8.4 replacement. Verify every target with dnf info php8.4-<name> or dnf list available 'php8.4-*' before you schedule removal—not every PHP 8.3 extension has an identical PHP 8.4 package name.
| PHP 8.3 package | PHP 8.4 replacement |
|---|---|
php |
php8.4 |
php-cli |
php8.4-cli |
php-common |
php8.4-common |
php-fpm |
php8.4-fpm |
php-mysqlnd |
php8.4-mysqlnd |
php-mbstring |
php8.4-mbstring |
php-xml |
php8.4-xml |
php-gd |
php8.4-gd |
php-intl |
php8.4-intl |
php-pgsql |
php8.4-pgsql |
php-ldap |
php8.4-ldap |
Hosts with additional packages such as php-soap, php-bcmath, php-process, or php-pecl-* must map and verify each one before removal.
Check application compatibility with PHP 8.4
Package replacement upgrades the PHP runtime—it does not upgrade your application code. Review compatibility before you remove PHP 8.3.
Check these areas on a staging copy when possible:
- PHP 8.4 backward-incompatible changes
- Deprecated features in PHP 8.4
- Removed extensions and extension behavior changes
- Framework, CMS, and plugin version requirements
- Custom PHP code, scheduled CLI scripts, and queue workers
- Composer platform requirements when the project contains
composer.json
Run these commands from the application directory when Composer is installed and the project contains composer.json.
Before upgrading, validate the lock file and identify packages that block PHP 8.4:
composer validatecomposer prohibits php 8.4The prohibits command (alias: composer why-not php 8.4) reports dependencies that forbid the target PHP version. It does not use the currently running PHP 8.3 runtime the way check-platform-reqs does.
Optionally review available dependency updates:
composer outdatedThat command lists newer package releases—it is not itself a PHP 8.4 compatibility test.
The full language migration belongs in the PHP 8.3 to 8.4 migration guide. This article covers Enterprise Linux package replacement only.
Back up PHP and web-server configuration
Back up the files your running stack actually uses—not only php.ini.
Archive core PHP configuration into /root/php83-upgrade/:
sudo cp -a /etc/php.ini /etc/php.d /root/php83-upgrade/When PHP-FPM is installed, back up the FPM configuration as well:
sudo cp -a /etc/php-fpm.conf /etc/php-fpm.d /root/php83-upgrade/Back up only the web server this host uses.
Apache host:
sudo cp -a /etc/httpd /root/php83-upgrade/httpd.bakNginx host:
sudo cp -a /etc/nginx /root/php83-upgrade/nginx.bakRecord runtime paths before services stop:
php --iniSave the effective PHP-FPM configuration before package replacement:
sudo php-fpm -tt 2>&1 | sudo tee /root/php83-upgrade/php-fpm-effective.txtSample output:
[22-Jul-2026 07:30:15] NOTICE: [global]
[22-Jul-2026 07:30:15] NOTICE: pid = /run/php-fpm/php-fpm.pid
[22-Jul-2026 07:30:15] NOTICE: error_log = /var/log/php-fpm/error.logThe command should exit successfully without an ERROR line. The saved file records the effective global and pool settings before package replacement.
Capture the systemd unit definition:
systemctl cat php-fpm | sudo tee /root/php83-upgrade/php-fpm.service.txt > /dev/nullRecord the pre-upgrade enablement state for the stack you use.
Apache host:
systemctl is-enabled php-fpm httpd 2>&1 | sudo tee /root/php83-upgrade/apache-service-state.txtNginx host:
systemctl is-enabled php-fpm nginx 2>&1 | sudo tee /root/php83-upgrade/nginx-service-state.txtAlso back up application files, Composer lock files, environment files, and databases when the upgrade may change application data. Do not plan to restore the entire PHP 8.3 configuration tree blindly over PHP 8.4.
Replace PHP 8.3 packages with PHP 8.4
Stop PHP-FPM and the web server you use.
For Apache:
sudo systemctl stop php-fpm httpdFor Nginx:
sudo systemctl stop php-fpm nginxPreview removal of the PHP 8.3 packages from your inventory. Adjust the list to match /root/php83-upgrade/dnf-packages.txt or dnf list --installed 'php*'. A host with packages such as php-ldap, php-pgsql, php-soap, php-bcmath, php-process, or php-pecl-* must include every installed PHP 8.3 RPM in the removal list—leftover extension packages can block the PHP 8.4 install.
sudo dnf remove --assumeno php php-cli php-common php-fpm \
php-gd php-intl php-mbstring php-mysqlnd php-opcache \
php-pdo php-xmlSample transaction summary:
Transaction Summary
================================================================================
Remove 47 Packages
Freed space: 72 M
Operation aborted.Read the full package list. DNF may propose removing httpd, font packages, or other dependencies installed only because PHP was present.
Remove the PHP 8.3 package set after you review the transaction:
sudo dnf remove php php-cli php-common php-fpm php-gd php-intl php-mbstring php-mysqlnd php-opcache php-pdo php-xmlInstall PHP 8.4 and the extensions you mapped earlier. The example below matches the tested inventory—change it to fit your host:
sudo dnf install php8.4 php8.4-fpm php8.4-mysqlnd php8.4-mbstring php8.4-xml php8.4-gd php8.4-intl php8.4-opcacheDo not copy that command unchanged. Every package name must come from /root/php83-upgrade/dnf-packages.txt and dnf list available 'php8.4-*' results.
Review and migrate PHP configuration
Compare the backed-up PHP 8.3 files with the newly installed PHP 8.4 defaults before you restart services.
Review the main CLI configuration:
php --iniInspect effective runtime values:
php -iValidate PHP-FPM syntax and dump the active configuration:
php-fpm -ttCompare individual files with diff when you need to merge a setting:
diff -u /root/php83-upgrade/php.ini /etc/php.iniTransfer only the settings you still need:
memory_limit,upload_max_filesize, andpost_max_size- Time zone and error-reporting levels
- Opcache settings under
/etc/php.d/ - Session storage paths
- PHP-FPM pool
user,group,listen, and process-manager values
Confirm the FPM socket or listener matches your web-server integration. Match the exact listen = assignment:
grep -E '^[[:space:]]*listen[[:space:]]*=' /etc/php-fpm.d/www.confSample output:
listen = /run/php-fpm/www.sockCheck for RPM-preserved configuration files after package changes:
sudo find /etc -type f \( -name '*.rpmnew' -o -name '*.rpmsave' \) -path '*php*'RPM may leave modified files as .rpmsave or install updated defaults as .rpmnew. Review those paths before you delete anything.
Start PHP 8.4 and verify the upgrade
Restore the service enablement state recorded before the upgrade. If PHP-FPM and the web server were enabled in /root/php83-upgrade/apache-service-state.txt or nginx-service-state.txt, enable and start them again. systemctl start brings a unit up for the current boot only; systemctl enable creates the boot-time symlinks Red Hat documents separately from startup.
When both services were enabled before package replacement:
Apache host:
sudo systemctl enable --now php-fpm httpdNginx host:
sudo systemctl enable --now php-fpm nginxWhen a service was intentionally disabled but not masked, use sudo systemctl start <unit> and leave its boot enablement unchanged. When it was masked before the upgrade, leave it stopped and masked unless you intentionally want to change that policy. A masked unit must be unmasked before it can be started.
Verify the CLI:
php -vSample output:
PHP 8.4.21 (cli) (built: May 5 2026 16:34:12) (NTS gcc x86_64)
Zend Engine v4.4.21, Copyright (c) Zend Technologies
with Zend OPcache v8.4.21, Copyright (c), by Zend TechnologiesExact patch and release strings vary by distribution update date. Confirm PHP 8.4.x—not a specific patch number shown here.
Confirm extensions:
php -mWhen the application uses Composer, verify platform requirements against the running PHP 8.4 runtime from the project directory:
composer check-platform-reqsThe command checks the real PHP 8.4 runtime and loaded extensions against the platform requirements of the installed Composer packages. It ignores any simulated config.platform value.
Validate FPM and the unit:
php-fpm -ttsystemctl status php-fpmTest the web stack with a temporary script. Run only the block for the web server used by this host.
Apache verification—document root /var/www/html:
echo '<?php echo "PHP " . PHP_VERSION . " via " . php_sapi_name(); ?>' | sudo tee /var/www/html/test.phpRequest the page with curl command:
curl -s http://127.0.0.1/test.phpSample output:
PHP 8.4.21 via fpm-fcgisudo rm /var/www/html/test.phpNginx verification—document root /usr/share/nginx/html:
echo '<?php echo "PHP " . PHP_VERSION . " via " . php_sapi_name(); ?>' | sudo tee /usr/share/nginx/html/test.phpcurl -s http://127.0.0.1/test.phpSample output:
PHP 8.4.21 via fpm-fcgisudo rm /usr/share/nginx/html/test.phpAfter package verification, test application behavior:
- Database connectivity and ORM queries
- File uploads and downloads
- Sessions and authentication
- Scheduled cron jobs and queue workers
- Composer commands in the project directory
- Administration pages and plugin modules
- PHP-FPM, web-server, and application error logs
Compare the current enablement state with the state saved before the upgrade.
Apache host:
systemctl is-enabled php-fpm httpdcat /root/php83-upgrade/apache-service-state.txtNginx host:
systemctl is-enabled php-fpm nginxcat /root/php83-upgrade/nginx-service-state.txtCompare each unit’s current enablement state with its saved pre-upgrade state. The values should match unless you intentionally changed the service policy; possible results include enabled, disabled, or masked.
Red Hat distinguishes disabling from masking: a disabled service can still be started manually, while a masked service cannot start until it is unmasked.
Troubleshoot failed PHP 8.4 upgrades
| Symptom | Main check |
|---|---|
php still reports PHP 8.3 |
rpm -qa 'php*', which php; finish removing unprefixed PHP 8.3 RPMs |
| DNF reports package conflicts | Remove remaining PHP 8.3 packages before installing php8.4-* |
| PHP extension is missing | Compare /root/php83-upgrade/php83-modules.txt with php -m; install the matching php8.4-* package |
| PHP-FPM fails to start | php-fpm -tt and sudo journalctl -u php-fpm |
Apache or Nginx returns 502 Bad Gateway |
Confirm php-fpm is active and the socket matches /run/php-fpm/www.sock |
| Application returns HTTP 500 | Inspect PHP-FPM, web-server, and application logs |
| Old PHP settings are ignored | php --ini, php-fpm -tt, and .rpmnew / .rpmsave files under /etc |
| Composer reports platform errors | Update dependencies or adjust config.platform.php after confirming runtime compatibility |
| Scheduled scripts fail | Confirm cron uses /usr/bin/php and required extensions are installed |
| SELinux blocks application access | Inspect /var/log/audit/audit.log before changing policy |
Useful inspection commands—see view logs with journalctl for unit filters and follow mode:
sudo journalctl -u php-fpmsudo journalctl -u httpdsudo journalctl -u nginxrpm -qa 'php*' | sortRoll back from PHP 8.4 to PHP 8.3
Roll back only when you have backups and accept that application or database changes made after the PHP 8.4 upgrade may need their own reversal.
Before you remove PHP 8.4, confirm that the PHP 8.3 packages from /root/php83-upgrade/dnf-packages.txt are still available in repository metadata:
dnf info php php-cli php-common php-fpmCheck every extension package from the saved inventory as well—for example dnf info php-mysqlnd php-xml php-gd.
Stop PHP-FPM and the web server you use.
Apache host:
sudo systemctl stop php-fpm httpdNginx host:
sudo systemctl stop php-fpm nginxRecord the installed PHP 8.4 packages:
dnf list --installed 'php8.4-*'Preview removal of the PHP 8.4 family:
sudo dnf remove --assumeno 'php8.4*'Sample transaction summary:
Transaction Summary
================================================================================
Remove 47 Packages
Freed space: 75 M
Operation aborted.Remove PHP 8.4 after you review the list:
sudo dnf remove 'php8.4*'Reinstall the PHP 8.3 packages listed in /root/php83-upgrade/dnf-packages.txt:
sudo dnf install php php-cli php-common php-fpm php-mysqlnd php-mbstring php-xml php-gd php-intl php-opcacheRestore compatible PHP 8.3 configuration from /root/php83-upgrade/—merge only the settings that still apply.
Restore service enablement the same way as after the PHP 8.4 install. When PHP-FPM and the web server were enabled before the upgrade, enable and start them again.
Apache host:
sudo systemctl enable --now php-fpm httpdsystemctl is-active php-fpm httpdNginx host:
sudo systemctl enable --now php-fpm nginxsystemctl is-active php-fpm nginxWhen a service was intentionally disabled but not masked, use sudo systemctl start <unit> and leave its boot enablement unchanged. When it was masked before the upgrade, leave it stopped and masked unless you intentionally want to change that policy. A masked unit must be unmasked before it can be started.
Only when you deliberately change that policy—not as part of the normal rollback—unmask first:
sudo systemctl unmask php-fpm
sudo systemctl start php-fpmCompare enablement against the saved state file when you need to confirm boot persistence.
Apache host:
systemctl is-enabled php-fpm httpdcat /root/php83-upgrade/apache-service-state.txtNginx host:
systemctl is-enabled php-fpm nginxcat /root/php83-upgrade/nginx-service-state.txtCompare each unit individually. Restore its original enabled, disabled, or masked state unless you deliberately want to change the boot policy.
Verify the rollback:
php -vSample output:
PHP 8.3.31 (cli) (built: May 5 2026 13:35:55) (NTS gcc x86_64)
Zend Engine v4.3.31, Copyright (c) Zend Technologies
with Zend OPcache v8.3.31, Copyright (c), by Zend TechnologiesExact patch and release strings vary by distribution update date. Confirm the major version returned to PHP 8.3.x rather than requiring the same patch numbers shown here.
On the tested host, removing php8.4-* and reinstalling the PHP 8.3 package set returned CLI 8.3.31.
References
- Red Hat Enterprise Linux 10 — Using the PHP scripting language
- RHEL 10.2 release notes — PHP 8.4 available
- Rocky Linux 10.2 release notes
- PHP migration guide from PHP 8.3 to PHP 8.4
- PHP 8.4 backward-incompatible changes
- PHP 8.4 deprecated features
- PHP 8.4 removed extensions
- Composer command-line interface —
prohibits,validate, andcheck-platform-reqs
Summary
Upgrading from PHP 8.3 to PHP 8.4 on Enterprise Linux is native package replacement: save package inventories under /root/php83-upgrade/, check application compatibility, back up PHP and web-server configuration, stop services, review DNF removal output, install matching php8.4-* packages, merge settings carefully, and verify CLI, PHP-FPM, Composer platform requirements, and the application. Roll back by removing php8.4-*, reinstalling PHP 8.3 from the saved inventory, and restoring compatible configuration.

