Understanding the AppStream module concept that lets one repository offer many application versions at once: listing, enabling, and switching module streams like Python, Node.js, and PostgreSQL, plus a comparison with the Software Collections legacy of the 8.x era.

In the previous episode, Episode 4, we got to know the BaseOS and AppStream repositories as package sources. Now we break down the most interesting part of AppStream: modules. A module is a feature that lets one repository offer several versions of the same application side by side — Python 3.9 alongside Python 3.12, Node.js 20 alongside Node.js 22, and so on.
This concept is the enterprise answer to the classic problem "one of my applications needs the old version, another needs the new version". In the RHEL 8 and 9 era it lives through module streams; in the older 8.x era there was also Software Collections (SCL). Let's understand both.
A module is a group of packages managed as a single unit — for example the python, nodejs, or postgresql module. Each module can have several streams, which are the different versions users can choose. There are also profiles that determine which set of packages gets installed.
An analogy: a module is the application brand, a stream is the variant or major version, and a profile is the edition (for example minimal, server, or development).
dnf5 module listThe output shows a table of modules along with their available streams — this is the software version menu on your system. To view the details of one module:
dnf5 module list pythonTo use a stream, you need to enable it first. Enabling determines which stream becomes the default when packages are installed from that module.
sudo dnf5 module enable python:3.12Once the stream is active, the related packages can be installed as usual:
sudo dnf5 install python3Tip
Important order: enable the stream first, then install the package. Installing a package from a module without enabling the stream can produce a version you don't want, and fixing that mistake later is more complicated.
sudo dnf5 module disable nodejs:22Disabling a module makes its stream unavailable for new installations — useful for locking down an environment so no package can pull that version.
Here are some modules most commonly used in the real world:
| Module | Example Streams | Common Use |
|---|---|---|
python | 3.12, 3.11, 3.9 | Python runtime for applications |
nodejs | 22, 20, 18 | Server-side JavaScript runtime |
postgresql | 16, 15, 13 | PostgreSQL database server |
nginx | 1.22, 1.20 | Web server and reverse proxy |
container-tools | latest | Podman, Buildah, Skopeo tooling |
dnf5 module list postgresqlYou can see that some streams are marked with status [e] (enabled), [d] (default), or [x] (disabled) — understanding these three statuses is the key to module management.
Needs change, and streams can be switched. The process is two steps: reset the module state, then enable the new stream.
sudo dnf5 module reset python
sudo dnf5 module enable python:3.12
sudo dnf5 upgradeWarning
Switching streams isn't a risk-free upgrade — it changes the runtime's major version. Applications that depend on the old version can break. Always test in a staging environment, and note that some streams only support switching through a specific path (for example upgrading the application on the application's own side).
Before AppStream modules, the RHEL ecosystem used Software Collections (SCL) — the old way to provide several software versions side by side on RHEL 6 and 7, and the early 8 era.
SCL's trademark: software is installed to a separate prefix (/opt/rh/), and activated by hooking up the environment:
scl enable rh-python36 -- python3 --versionPopular collections from that era: gcc-toolset (modern compiler toolchain), rh-python36, and devtoolset. The output of the command above shows the Python version enabled only for the duration of that command.
| Aspect | SCL | AppStream Module |
|---|---|---|
| Install location | Separate /opt/rh/ prefix | Standard /usr/ location |
| Activation | scl enable command | Global activation via dnf |
| Version management | Separate collections | Streams within one module |
| Modern way? | 8.x legacy | Standard in 9/10 |
Key conclusion: on AlmaLinux 9 and 10, the correct way is AppStream modules — not SCL. SCL is largely retired on RHEL 9+, and you won't find scl promoted as a recommended practice. However, understanding SCL matters for managing 8.x servers still running in the field, especially because AlmaLinux 8 remains under maintenance until 2029.
latest stream and don't need enabling.dnf5 module reset. Without a reset, enabling a new stream often fails because the old state is still attached.In this episode 5 you've understood AppStream modules: the module, stream, and profile concepts, how to list, enable, disable, and switch streams, examples of popular modules like Python, Node.js, and PostgreSQL, and an honest comparison between modern modules and the Software Collections legacy of the 8.x era.
Key takeaways:
dnf5 module enable <name>:<stream>, then install its packages.module reset → module enable → upgrade sequence.python:3.12, nodejs:20, postgresql:16.gcc-toolset) is an 8.x legacy — use AppStream modules on 9/10.Tidy software version management is a hallmark of a mature sysadmin. In the next episode, Episode 6, we'll cover Users, Permissions & Sudo — from creating users and groups, the /etc/passwd and /etc/shadow identity files, to sudo configuration, the wheel group, ACLs, and the SUID/SGID/sticky special bits. See you there!