AlmaLinux 10: from module streams to versioned packages
Test scope
Section titled “Test scope”- EL9 side: an
almalinux:9container with full AppStream module metadata; - EL10 side: an AlmaLinux 10.2 VM (dnf 4.20.0) and an
almalinux:10container.
Both sides ran dnf module list, dnf module list nodejs, dnf module enable, and dnf list --available to locate replacements.
Log: plans/content-expansion/evidence/logs/module-streams-el9-el10.log.
EL9: one package, several version tracks
Section titled “EL9: one package, several version tracks”$ dnf module listmariadb 10.11 client, galera, server [d]mariadb 11.8 client, galera, server [d]mysql 8.4 api, client, filter, server [d]nginx 1.22 / 1.24 / 1.26 common [d]nodejs 18 / 20 / 22 / 24 common [d], development, minimal, s2iphp 8.1 / 8.2 / 8.3 common [d], devel, minimal[d] marks the default profile, and installed RPMs carry a module build tag:
$ rpm -q nodejsnodejs-22.23.2-2.module_el9.8.0+322+f4aa738c.x86_64EL10: command present, capability absent
Section titled “EL10: command present, capability absent”$ dnf module listWARNING: modularity is deprecated, and functionality will be removed in a future release of DNF5.
$ dnf module list nodejsWARNING: modularity is deprecated, and functionality will be removed in a future release of DNF5.Error: No matching Modules to list
$ dnf module enable nodejs:20Error: Problems in request:missing groups or modules: nodejs:20Worth stressing: EL10 still ships dnf 4.20, not dnf5 — so this is not “new dnf has not implemented it yet”, it is modularity being retired.

Parallel versions move into package names
Section titled “Parallel versions move into package names”$ dnf -q list --available 'php8*'php8.4.x86_64 8.4.24-1.el10_2 appstreamphp8.4-bcmath.x86_64 8.4.24-1.el10_2 appstream| EL9 | EL10 |
|---|---|
| php streams 8.1 / 8.2 / 8.3 | php defaults to 8.3; php8.4 installs alongside |
| nodejs streams 18/20/22/24 | a single nodejs track (22.x) |
dnf module enable X:Y | gone; install the versioned package instead |
The gain is clarity — the name states the version and dnf list shows everything. The cost is losing in-place track switching, so automation must be rewritten.
Three checks before migrating
Section titled “Three checks before migrating”grep -rn "module \(enable\|install\|reset\)" /etc/ansible /opt/scripts 2>/dev/null | headdnf list --installed | grep -c module_el || truednf -q list --available 'php8*' nodejs 2>/dev/null | head -5The first finds scripts to fix, the second counts modules-era packages on the old host, the third shows which versioned names exist on the new one.
Common traps
Section titled “Common traps”- Reading the silence as a repo problem: an empty list after the warning is normal EL10 output;
- Copying EL9 Ansible roles:
dnf: name=@nodejs:20/commonfails outright; - Hunting streams in other repos: EL10 repos ship no module metadata anywhere;
- Comparing package lists after upgrade: a module-tagged build and a plain package are the same software packaged differently.
Practice: confirm EL10 has no streams
Section titled “Practice: confirm EL10 has no streams”dnf module list 2>&1 | tail -3dnf module enable nodejs:20 2>&1 | tail -2dnf -q list --available nodejs 2>/dev/null | head -3Checks:
- The first prints only the warning, no table;
- The second reports
missing groups or modules; - The third lists concrete
nodejsversions; - Conclusion: EL10 encodes versions in package names.
常见问题
Why was modularity removed?
Upstream RHEL 10 retired it because parallel streams complicated repositories and upgrades; parallel-installable versioned packages replace it.
Can I still use streams on EL9?
Yes for the EL9 lifetime, but upgrading to EL10 requires rewriting those commands first.
What if I need nodejs 18 on EL10?
Run it in a container or take it from a third-party repository such as NodeSource; official EL10 repos keep only the current major.
Next steps
Section titled “Next steps”- Repository audit: /en/repo-priority/
- Offline installs: /en/offline-updates/
- Versions and support: /en/versions/