The Remi repository gives you every PHP branch from 7.4 to the newest release on RHEL, Rocky Linux and AlmaLinux. Each branch is a module stream you switch with one dnf command. EL10 shipped with only the 8.3 line in AppStream, and 10.2 adds php8.4 packages. Remi is where you get 8.5 and the old 7.4 to 8.2 branches. Its parallel builds such as php84 also run next to the system PHP, which is the cleanest way to test an upgrade.
Below you will install and enable the Remi repository, pick a stream, run two PHP versions side by side and switch back down. On our Rocky Linux 10.1 server, remi-release also quietly upgraded the OS release package to 10.2. We tested on Rocky Linux 10 and in an AlmaLinux 9.8 container in October 2026, with PHP 8.5.11 and 8.4.26 from Remi.
Install the Remi repository
Remi publishes one remi-release package per major release, and the URL below picks the right one for you because rpm -E %rhel expands to 10 on an EL10 box and 9 on EL9. Our Rocky Linux server started with only the three stock repositories enabled and no EPEL installed, so every change you see below came from the Remi package. Check your own starting point the same way, because a box that already has EPEL or an old Remi release will show a different transaction.
cat /etc/rocky-release; dnf repolist
The box was on Rocky Linux 10.1 with BaseOS, AppStream and Extras only. Keep a note of that release line, because it does not stay the same after the next step.
Rocky Linux release 10.1 (Red Quartz)
repo id repo name
appstream Rocky Linux 10 - AppStream
baseos Rocky Linux 10 - BaseOS
extras Rocky Linux 10 - Extras
Install the release package straight from Remi’s server. You do not need to install EPEL first on Rocky Linux or AlmaLinux, because remi-release depends on epel-release and dnf pulls it in from the Extras repository on its own. If you already followed our EPEL setup for Rocky Linux and AlmaLinux, dnf simply keeps the copy you have.
sudo dnf -y install https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
Read the end of the transaction, not just the word Complete. Next to remi-release and epel-release, dnf also upgraded three Rocky Linux packages from 10.1 to 10.2, and nothing in the command asked for that.
Upgraded:
rocky-gpg-keys-10.2-1.2.el10.noarch rocky-release-10.2-1.2.el10.noarch
rocky-repos-10.2-1.2.el10.noarch
Installed:
epel-release-10-7.el10_1.noarch remi-release-10.2-1.el10.remi.noarch
Complete!
The reason is in the package’s own dependencies. The current remi-release for EL10 requires a release package of at least 10.2, so on a 10.1 box dnf upgrades rocky-release to satisfy it, and rocky-repos and rocky-gpg-keys moved with it in the same transaction. Nothing else on the system changed, which is why it is easy to miss. You can confirm what the package asks for:
rpm -q --requires remi-release | grep -v rpmlib
The first line is the one that moved the release package. The epel-release = 10 line explains why EPEL arrived without being asked for, and the last one ties the package to the EL10 major release, so it can never install on EL9.
(redhat-release >= 10.2 or centos-stream-release >= 10)
config(remi-release) = 10.2-1.el10.remi
epel-release = 10
system-release(releasever) = 10
On an ordinary server that tracks the latest minor release this does no harm, because your next dnf update would have moved it to 10.2 anyway. On a host you hold at a minor release on purpose, it is a change you did not ask for. Drop the -y on those machines so dnf shows you the transaction and you can say no.
On RHEL itself the one-liner fails, because Red Hat’s repositories have no epel-release package for dnf to pull in. Remi’s configuration wizard gives this sequence for a subscribed RHEL host: enable CodeReady Builder, which many EPEL packages depend on, install EPEL from Fedora’s URL, then install remi-release. We did not run it, since our lab had no RHEL subscription:
sudo subscription-manager repos --enable codeready-builder-for-rhel-$(rpm -E %rhel)-$(arch)-rpms
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(rpm -E %rhel).noarch.rpm
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
The rest of this guide applies to RHEL unchanged once those three packages are in place, apart from the crb step below, which the first command already covers.
Error: “nothing provides epel-release = 9 needed by remi-release-9”
This is what you get when you paste a URL with a hard-coded release number from an older guide. We installed remi-release-9.rpm on the same Rocky Linux 10 box to capture it.
sudo dnf -y install https://rpms.remirepo.net/enterprise/remi-release-9.rpm
dnf refuses it, because the EL9 package wants EL9’s EPEL and an EL9 release. Nothing is installed, so there is nothing to clean up.
Error:
Problem: conflicting requests
- nothing provides epel-release = 9 needed by remi-release-9.8-1.el9.remi.noarch from @commandline
- nothing provides system-release(releasever) = 9 needed by remi-release-9.8-1.el9.remi.noarch from @commandline
(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)
Do not reach for --skip-broken here, since it only skips the package you asked for. Use the $(rpm -E %rhel) form of the URL, which always matches the running major release. The same mismatch is behind the old remi-release-8.rpm line that CentOS 8 guides still carry.
Verify the enabled Remi repositories
The release package drops several repo files into /etc/yum.repos.d/ but enables only two Remi repositories, and that split is deliberate. Check what is active now:
dnf repolist
EPEL, remi-modular and remi-safe are new. If remi-modular is missing from your list, the release package did not install, so go back to the transaction output before you try any PHP command. A missing epel line points to the same cause, because EPEL only arrives as a dependency of remi-release.
repo id repo name
appstream Rocky Linux 10 - AppStream
baseos Rocky Linux 10 - BaseOS
epel Extra Packages for Enterprise Linux 10 - x86_64
extras Rocky Linux 10 - Extras
remi-modular Remi's Modular repository for Enterprise Linux 10 - x86_64
remi-safe Safe Remi's RPM repository for Enterprise Linux 10 - x86_64
Each of those two has one job, and neither replaces a system package until you ask it to:
remi-modular: thephpmodule streams that replace the system PHPremi-safe: parallel builds such asphp84under/opt/remiremi: newer libraries and tools, disabled by defaultremi-testandremi-modular-test: test builds, disabled
Leave the disabled ones alone for now. The last section of this guide shows how to pull a single package from remi without turning it on for every update.
Enable CRB for EPEL dependencies
Many EPEL packages, and some of Remi’s extensions, depend on libraries that only live in the CRB repository, which Rocky Linux and AlmaLinux ship disabled. Without it, dnf fails on a missing -devel or library package that is nowhere in the enabled repos, so the error looks like a broken repository. The epel-release package that came in with Remi includes a small crb helper, so you do not have to remember the repo id.
rpm -qf /usr/bin/crb
sudo crb enable
The helper belongs to EPEL, and it reports the repo it switched on. On RHEL the equivalent repo is CodeReady Builder, which the subscription-manager command above enables instead.
epel-release-10-7.el10_1.noarch
Enabling CRB repo
CRB repo is enabled and named: crb
Our PHP installs ran before we enabled CRB and resolved fine, so treat this as the step that saves you a confusing dependency error the first time you install something larger from EPEL. The CRB repository guide covers the manual dnf config-manager way if you would rather not depend on the helper.
Choose a PHP module stream
Remi delivers PHP as dnf module streams named remi-X.Y, one per branch, and you enable exactly one at a time. dnf allows one enabled stream per module, so every php-* package resolves to that branch. You can’t end up with a mix of 8.4 and 8.5 packages. List the streams before you pick:
dnf module list php
On Rocky Linux 10 every stream comes from Remi, from 7.4 up to 8.6. The deprecation warning at the top comes from dnf itself and does not stop modules from working on EL10, so you can ignore it for now. It’s a notice about dnf’s own future, not an error in Remi’s repository.
WARNING: modularity is deprecated, and functionality will be removed in a future release of DNF5.
Remi's Modular repository for Enterprise Linux 10 - x86_64
Name Stream Profiles Summary
php remi-7.4 common [d], devel, minimal PHP scripting language
php remi-8.0 common [d], devel, minimal PHP scripting language
php remi-8.1 common [d], devel, minimal PHP scripting language
php remi-8.2 common [d], devel, minimal PHP scripting language
php remi-8.3 common [d], devel, minimal PHP scripting language
php remi-8.4 common [d], devel, minimal PHP scripting language
php remi-8.5 common [d], devel, minimal PHP scripting language
php remi-8.6 common [d], devel, minimal PHP scripting language
Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled
EL10 and EL9 differ here, which is the part most older Remi guides get wrong for the newer release. EL10’s AppStream has no PHP module at all. Its default php package is the 8.3 line, and from 10.2 AppStream also ships separately named, non-modular php8.4 packages. Check what your release offers:
cat /etc/rocky-release
dnf -q list --available "php8*" | grep -E "^php8\.[0-9]+(-cli|-fpm)?\."
Our 10.2 box listed PHP 8.4 from appstream, with 38 php8* packages available in total on a stock box without Remi. If 8.4 is all you need on 10.2, that package set keeps you on Red Hat’s support and errata, and you may not need Remi at all.
Rocky Linux release 10.2 (Red Quartz)
php8.4.x86_64 8.4.24-1.el10_2 appstream
php8.4-cli.x86_64 8.4.24-1.el10_2 appstream
php8.4-fpm.x86_64 8.4.24-1.el10_2 appstream
EL9’s AppStream, by contrast, still offers its own PHP module streams beside Remi’s:
| Item | Rocky Linux 10 | AlmaLinux 9 |
|---|---|---|
| Release package | remi-release-10.2-1.el10.remi | remi-release-9.8-1.el9.remi |
| EPEL pulled in | epel-release-10-7.el10_1 | epel-release-9-9.el9 |
| AppStream PHP | 8.3 default, not modular; 10.2 adds php8.4-* packages | Module streams 8.1, 8.2, 8.3 (as listed by our module filter) |
| Remi streams | remi-7.4 to remi-8.6 | remi-7.4 to remi-8.6 |
| Module reset before enable | Not needed on our fresh box | We ran dnf module reset php first |
Pick remi-8.4 or remi-8.5 for anything you run in production, and match the branch your application supports. Stay off remi-8.6 for now: when we checked in October 2026, the stream carried php-cli-8.6.0~RC2, a release candidate. The 7.4 to 8.1 streams exist for legacy code that you can’t move yet, and PHP upstream no longer patches those branches, so treat them as a bridge while you port, not a place to stay.
Install PHP from the Remi module
Enable the stream first, then install the packages, because the stream decides which PHP version every php-* package resolves to. We enabled 8.5 and installed the CLI, FPM and the extensions a typical web app needs:
sudo dnf -y module enable php:remi-8.5
sudo dnf -y install php-cli php-fpm php-mysqlnd php-opcache php-pecl-redis6
The transaction shows every PHP package coming from remi-modular with a module_php.8.5 release tag, which is how you confirm the stream took effect. The Redis extension pulled in igbinary and msgpack as dependencies, so you do not install those serializers by hand.
php-cli x86_64 8.5.11-1.module_php.8.5.el10.remi remi-modular 6.0 M
php-pecl-redis6 x86_64 6.3.0-1.module_php.8.5.el10.remi remi-modular 269 k
php-pecl-igbinary x86_64 3.2.16-6.module_php.8.5.el10.remi remi-modular 60 k
php-pecl-msgpack x86_64 3.0.1-1.module_php.8.5.el10.remi remi-modular 41 k
Check the version and that the three extensions load. A missing extension here usually means the package came from a different stream than the one you enabled.
php -v | head -1
php -m | grep -ciE "^(redis|mysqlnd|Zend OPcache)$"
You get PHP 8.5 and a count of 4, which is right. OPcache is listed twice in php -m, once under PHP Modules and once under Zend Modules, so three extensions produce four matches. Remi’s wizard gives sudo dnf module switch-to php:remi-8.5 as the official one-step form, which enables the stream and moves any installed PHP packages to it; we used it for the switch in the last section.
PHP 8.5.11 (cli) (built: Sep 22 2026 13:32:06) (NTS gcc x86_64)
4
On EL9, AppStream has php streams of its own, so reset the module before you enable a Remi stream. That clears any stream picked earlier, and in our AlmaLinux 9 container the sequence installed PHP 8.4 from remi-8.4 cleanly:
sudo dnf -y module reset php
sudo dnf -y module enable php:remi-8.4
sudo dnf -y install php-cli
For a full PHP stack with FPM pools, Nginx and the common extensions on top of this repository, follow the PHP 8.5 install guide for Rocky Linux and AlmaLinux. This page stops at the repository and the stream.
Run a second PHP version side by side
The module stream replaces the system PHP, so you can only have one of those. When you want to test an application on another branch without touching the running one, use the parallel builds in remi-safe instead. They install under /opt/remi with their own binary name, and that is why they never conflict.
sudo dnf -y install php84-php-cli
Three packages come from remi-safe, including a small runtime package that owns the /opt/remi/php84 tree. None of them touch the php-* packages the module installed.
php84-php-cli x86_64 8.4.26-1.el10.remi remi-safe 2.3 M
php84-php-common x86_64 8.4.26-1.el10.remi remi-safe 759 k
php84-runtime x86_64 8.4-3.el10.remi remi-safe 1.1 M
Complete!
Call each one by name and compare. The parallel build keeps the version in its command name, so scripts have to call php84 explicitly.
php84 -v | head -1; php -v | head -1; ls /opt/remi/
Both versions answer on the same host, and the system php is still 8.5. The /opt/remi listing shows only php84, because that is the only parallel build installed.
PHP 8.4.26 (cli) (built: Sep 22 2026 15:00:02) (NTS gcc x86_64)
PHP 8.5.11 (cli) (built: Sep 22 2026 13:32:06) (NTS gcc x86_64)
php84
Our terminal from the web01 lab server shows the whole picture in one place: the streams Remi offers, the module PHP and the parallel build.

We would use this layout for a staged upgrade: keep production on the module stream, run your test suite against the php84 build, and only then switch the stream. The cost is that extensions have to be installed per build, so php84-php-mysqlnd is a separate package from php-mysqlnd.
Use the disabled remi repository per command
The plain remi repository ships newer builds of libraries and tools that overlap with the base OS, which is why the release package leaves it off. Turning it on permanently means a routine dnf update can replace system libraries, so enable it for one command at a time with --enablerepo. Check what it would add for a few packages PHP apps commonly need:
dnf -q --enablerepo=remi list --available | grep -E "^(redis|valkey|memcached|ImageMagick|composer|libzip|php-cli)\."
Only ImageMagick came from remi on our EL10 box. Composer resolved from EPEL, and libzip, memcached and Valkey from AppStream, so for those you do not need Remi at all. That listing does not show everything Remi has: remi-modular also carries redis, valkey, memcached and composer module streams, which stay hidden until you enable one.
ImageMagick.x86_64 1:7.1.2.34-1.el10.remi remi
composer.noarch 2.10.3-1.el10_2 epel
libzip.x86_64 1.10.1-5.el10 appstream
memcached.x86_64 1.6.23-7.el10_2.1 appstream
valkey.x86_64 8.0.11-1.el10_2 appstream
That is the pattern to follow. Install the one package you need with sudo dnf --enablerepo=remi install ImageMagick and leave the repo disabled (we listed it but did not install it). Updates need the same flag, sudo dnf --enablerepo=remi update ImageMagick, because a plain dnf update won’t see the repo. Reach for the distro’s own Valkey (our Valkey install guide covers it) before any third-party Redis build. The dnf package manager guide explains --enablerepo, priorities and repo exclusions in more depth.
Roll back to AppStream PHP or remove Remi
Moving between Remi branches is one command. module switch-to changes the enabled stream and downgrades or upgrades every installed PHP package in the same transaction:
sudo dnf -y module switch-to php:remi-8.4
php -v | head -1
dnf module list --enabled php
Going from 8.5 to 8.4 downgraded 8 packages, php -v reports the 8.4 build, and the module list marks the new stream [e] for enabled. Run php -m afterwards to confirm the extensions you rely on are still loaded.
Downgrading:
Downgrade 8 Packages
Downgrading : php-common-8.4.26-1.module_php.8.4.el10.remi.x86_6 1/16
Downgrading : php-pdo-8.4.26-1.module_php.8.4.el10.remi.x86_64 2/16
PHP 8.4.26 (cli) (built: Sep 22 2026 15:00:02) (NTS gcc x86_64)
php remi-8.4 [e] common [d], devel, minimal PHP scripting language
Restart php-fpm after a switch, because the running workers keep the old binary until systemd replaces them. A switch inside Remi is cheap to undo, so it is the safe way to try a newer branch.
Going all the way back to AppStream PHP is a different job, and the obvious two commands don’t do it. We ran the rollback on the same Rocky Linux 10 box only, so everything below is EL10; it started from PHP 8.4 out of remi-8.4 with the Redis extension and the php84 parallel build still installed. This is the whole sequence that worked, in order:
sudo dnf -y module reset php
sudo dnf distro-sync --allowerasing 'php*'
sudo dnf -y remove 'php84-*' remi-release
sudo dnf -y distro-sync 'php*'
The second command has no -y on purpose. --allowerasing removes whatever conflicts, and anything on your box that depends on php-common can end up in that list, so read the removal list before you answer yes. On 10.2 you could instead land on AppStream’s php8.4 packages rather than the default 8.3 line; we did not test that path. The steps below show why each command is there.
- Reset the php module stream
- Erase the Remi PECL extensions that block the downgrade
- Remove the parallel builds and
remi-release - Sync the remaining PHP packages to AppStream
Error: “cannot install both php-common-8.3.33-1.el10_2 from appstream and php-common-8.4.26 from @System”
This is what the textbook rollback gives you while Remi’s PECL extensions are installed. Resetting the module and running distro-sync looks right, and dnf even prints Complete for the reset:
sudo dnf -y module reset php
sudo dnf -y distro-sync 'php*'
The sync itself fails. This excerpt keeps the interesting lines of a much longer problem list: the conflict on php-common and the EPEL build of msgpack, which is compiled against the 8.3 ABI and can’t be satisfied while the 8.4 packages are installed:
Problem 4: package php-pecl-msgpack-3.0.1-2.el10_2.x86_64 from epel requires php(api) = 20230831-64, but none of the providers can be installed
- cannot install both php-common-8.3.33-1.el10_2.x86_64 from appstream and php-common-8.4.26-1.module_php.8.4.el10.remi.x86_64 from @System
- package php-pdo-8.4.26-1.module_php.8.4.el10.remi.x86_64 from @System requires php-common(x86-64) = 8.4.26-1.module_php.8.4.el10.remi, but none of the providers can be installed
- php-pecl-msgpack-3.0.1-1.module_php.8.4.el10.remi.x86_64 from @System does not belong to a distupgrade repository
- package php-common-8.4.26-1.module_php.8.4.el10.remi.x86_64 from remi-modular is filtered out by modular filtering
(try to add '--allowerasing' to command line to replace conflicting packages or '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)
PHP was still 8.4 afterwards, so nothing broke, but nothing moved either. Take the hint and add --allowerasing, which lets dnf remove the conflicting extensions. Leave off -y and read the removal list first:
sudo dnf distro-sync --allowerasing 'php*'
On our box it erased exactly three packages, the Redis extension and the two serializers it pulled in. It did not downgrade PHP, and a second plain distro-sync answered “Nothing to do” with 8.4 still in place while remi-release was installed:
Erasing : php-pecl-redis6-6.3.0-1.module_php.8.4.el10.remi.x86 1/3
Erasing : php-pecl-igbinary-3.2.16-2.el10.remi.8.4.x86_64 2/3
Erasing : php-pecl-msgpack-3.0.1-1.module_php.8.4.el10.remi.x8 3/3
Removed:
Complete!
Next, remove the parallel builds and remi-release in one transaction, then run the sync again:
sudo dnf -y remove 'php84-*' remi-release
sudo dnf -y distro-sync 'php*'
php -v | head -1
Removing them took out 10 packages, and after that the sync downgraded the five remaining PHP packages to AppStream’s 8.3 build:
php-cli x86_64 8.3.33-1.el10_2 appstream 3.6 M
php-common x86_64 8.3.33-1.el10_2 appstream 708 k
php-fpm x86_64 8.3.33-1.el10_2 appstream 1.9 M
php-mysqlnd x86_64 8.3.33-1.el10_2 appstream 137 k
php-pdo x86_64 8.3.33-1.el10_2 appstream 85 k
Downgrade 5 Packages
Downgraded:
Complete!
PHP 8.3.33 (cli) (built: Jul 28 2026 17:56:10) (NTS gcc x86_64)
Restart php-fpm after the downgrade and test your application before you call it done, because the Redis extension is gone and code that calls it will fail at runtime. If you need it back, AppStream has its own build, so no third-party repo is involved: sudo dnf -y install php-pecl-redis6 installed version 6.1.0 from appstream, and php -m listed redis again on PHP 8.3.
Check what the removal left behind:
rpm -q epel-release rocky-release remi-release
EPEL is gone too, because dnf removed epel-release along with remi-release as an unused dependency. The Rocky Linux release stays at 10.2:
package epel-release is not installed
rocky-release-10.2-1.2.el10.noarch
package remi-release is not installed
If anything else on the box needs EPEL, reinstall it with sudo dnf -y install epel-release straight after the rollback (it came back from Extras as 10-7.el10_1 in our test), before the next update tries to resolve those packages. No Remi repo files were left in /etc/yum.repos.d/ and no php module stream was enabled, so the server is back to stock PHP. The 10.2 release upgrade stays, and you shouldn’t try to undo it, since it’s the same update every unpinned server receives.