Why and how to start with the system Ruby modular packages

Modular packages fixes the biggest complain of a single system Ruby package by giving you more options. Let’s see how to install different Ruby versions from modular packages in Fedora, CentOS Stream or Rocky Linux.

Although I am a fan of chruby (read my comparison of ruby-build with ruby-install), long compile times is something I dread. A lot of times, I just used system packages.

System packages are fast to install (no compilation!) and easy to maintain (free security updates!), but there was one thing that prevents most to use them: a single version in the repository. This is exactly what modular packages are trying to fix.

Since Fedora 28 times we got one extra RPM repository called Fedora Modular. It’s a repository that lets you install this modular packages and should be installed for you by default. The repository is called AppStream on RHEL, CentOS Stream, and Rocky Linux. But essentially it’s the same thing.

With dnf module list we can see all available modules. I’ll limit it to Ruby:

$ dnf module list | grep ruby
ruby    2.7      default [d]      An interpreter of object-oriented scripting language               
ruby    3.0      default [d]      An interpreter of object-oriented scripting language

Now we are talking! Instead of just one version we got two; one of the 2.7.x line and one with the new Ruby 3.0. Yes, we don’t get every single Ruby because the maitainance burden would be too high, but now have options.

Note that the output above is from fresh Fedora 35. Also note that there is still a system Ruby available in the base ruby package. But back to modules.

The output above list the module name (ruby) and stream (2.7 and 3.0), so the version is not really a version but rather a stream.

To install a modular package we enable a module and install it:

$ sudo dnf module enable module-name
$ sudo dnf module install module-name

Alternatively we specify the stream we want:

$ sudo dnf module enable module-name:stream
$ sudo dnf module install module-name:stream

So to install Ruby 2.7, I first install the 2.7 stream of the ruby module:

$ sudo dnf module enable ruby:2.7
$ sudo dnf module install ruby:2.7

The command module install will install the default profile (set of packages). Once the stream is enabled, it’s also possible to install its RPM packages with usual dnf install. Stream packages would be prefered to base system packages.

Once done, we should be able to use Ruby 2.7:

$ ruby -v
ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x86_64-linux]

Compiling Ruby is my single longest step in updating my servers or rebuilding my Docker containers. With modular packages, we get Ruby almost instantly.

We could later remove the module with:

$ sudo dnf module remove module-name

Once we want to switch to a different stream, we have to run reset before enabling the new stream and distro-sync afterwards:

$ sudo dnf module reset module-name
$ sudo dnf module enable module-name:stream
$ sudo dnf module install module-name:stream
$ sudo dnf distro-sync

While modularity offers more versions, streams of one module are not meant to be used together. You need to decide what version you want, rather than using them to develop many projects at once.


I wrote a complete guide on web application deployment. Ruby with Puma, Python with Gunicorn, NGINX, PostgreSQL, Redis, networking, processes, systemd, backups, and all your usual suspects.

More →