Skip to content

Manage DNF Repositories and Module Streams

In DNF, repositories, module streams, versionlock, and the metadata cache each affect the selection of package candidates. By checking these elements separately, it becomes easier to control package sources and installed versions within the intended range.

This article targets RHEL 8-compatible environments and uses Node.js from AppStream as an example. It covers enabling and disabling repositories, checking package sources with repoquery, selecting a module stream, locking versions with versionlock, and rebuilding the cache.

To verify how versionlock suppresses updates in practice, package installation will use an older build that remains available in the same stream rather than the latest version currently available. Because this operation is intended to verify version-locking behavior, check compatibility with existing applications before performing it in a production environment.

Values that vary depending on the environment or target package are represented by the following variables.

Variable nameExample settingDescription
<<REPO_ID>>appstreamID of the DNF repository to operate on
<<MODULE_NAME>>nodejsName of the module to check and enable
<<MODULE_STREAM>>22Module stream to enable and use for package installation
<<MODULE_PACKAGE>>nodejsPackage to install from the module stream and manage with versionlock

Prepare config-manager for switching repository settings and versionlock for locking package versions.

Terminal window
sudo dnf install -y dnf-plugins-core python3-dnf-plugin-versionlock

Step 2: Check the current repository status

Section titled “Step 2: Check the current repository status”

Review the repository list, including both enabled and disabled repositories, and confirm that the repository ID you want to operate on exists.

Terminal window
sudo dnf repolist --all

Disable the target repository and confirm that it appears in the list of disabled repositories.

Terminal window
sudo dnf config-manager --set-disabled "<<REPO_ID>>"
disabled_repos="$(sudo dnf repolist --disabled)"
printf '%s\n' "$disabled_repos"
printf '%s\n' "$disabled_repos" | grep -E "^<<REPO_ID>>[[:space:]]"

Re-enable the target repository for subsequent package operations and confirm that it has returned to the list of enabled repositories.

Terminal window
sudo dnf config-manager --set-enabled "<<REPO_ID>>"
enabled_repos="$(sudo dnf repolist --enabled)"
printf '%s\n' "$enabled_repos"
printf '%s\n' "$enabled_repos" | grep -E "^<<REPO_ID>>[[:space:]]"

Display the stream list for the target module and confirm that <<MODULE_STREAM>> is available.

Terminal window
module_output="$(sudo dnf module list "<<MODULE_NAME>>")"
printf '%s\n' "$module_output"
printf '%s\n' "$module_output" | grep -E "^<<MODULE_NAME>>[[:space:]]+<<MODULE_STREAM>>([[:space:]]|$)"

Step 6: Reset an existing stream selection

Section titled “Step 6: Reset an existing stream selection”

Reset the target module to its initial state so that the same procedure can be started even in an environment where a module stream was previously selected. This operation alone does not remove installed packages.

Terminal window
sudo dnf module reset -y "<<MODULE_NAME>>"

Enable <<MODULE_STREAM>> and confirm that the target stream is marked with [e], indicating that it is enabled.

Terminal window
sudo dnf module enable -y "<<MODULE_NAME>>:<<MODULE_STREAM>>"
module_output="$(sudo dnf module list "<<MODULE_NAME>>")"
printf '%s\n' "$module_output"
printf '%s\n' "$module_output" | grep -E "^<<MODULE_NAME>>[[:space:]]+<<MODULE_STREAM>>[[:space:]]+\[e\]"

Step 8: Check available versions and sources with repoquery

Section titled “Step 8: Check available versions and sources with repoquery”

By specifying --repo, you can limit that DNF command to only the specified repository. For packages available in the currently enabled stream, display the version, release, architecture, and source repository.

Terminal window
query_output="$(sudo dnf --repo "<<REPO_ID>>" repoquery --qf '%{name} %{version}-%{release}.%{arch} %{reponame}' "<<MODULE_PACKAGE>>")"
printf '%s\n' "$query_output"
printf '%s\n' "$query_output" | grep -E "^<<MODULE_PACKAGE>>[[:space:]]+<<MODULE_STREAM>>\..*[[:space:]]+<<REPO_ID>>$"

Step 9: Install an older build from the same stream

Section titled “Step 9: Install an older build from the same stream”

To verify update suppression with versionlock, select and install one candidate while excluding the latest build. Confirm that the latest version differs from the installed version and that the installed package belongs to the selected stream.

Terminal window
arch="$(uname -m)"
latest_vr="$(sudo dnf --repo "<<REPO_ID>>" repoquery --arch "$arch" --latest-limit=1 --qf '%{version}-%{release}' "<<MODULE_PACKAGE>>" | tail -n 1)"
older_nevra="$(sudo dnf --repo "<<REPO_ID>>" repoquery --arch "$arch" --latest-limit=-1 "<<MODULE_PACKAGE>>" | tail -n 1)"
test -n "$latest_vr"
test -n "$older_nevra"
sudo dnf install -y "$older_nevra"
installed_vr="$(sudo rpm -q --qf '%{VERSION}-%{RELEASE}\n' "<<MODULE_PACKAGE>>")"
printf '%s\n' "$installed_vr"
printf '%s\n' "$installed_vr" | grep -E "^<<MODULE_STREAM>>\."
test "$installed_vr" != "$latest_vr"
sudo dnf module list "<<MODULE_NAME>>"

Step 10: Lock the installed version with versionlock

Section titled “Step 10: Lock the installed version with versionlock”

Register the currently installed package with versionlock. After registration, confirm that the target package appears in the lock list.

Terminal window
sudo dnf versionlock add "<<MODULE_PACKAGE>>"
lock_output="$(sudo dnf versionlock list)"
printf '%s\n' "$lock_output"
printf '%s\n' "$lock_output" | grep -F "<<MODULE_PACKAGE>>"

Step 11: Apply the repository, stream, and versionlock together

Section titled “Step 11: Apply the repository, stream, and versionlock together”

Reinstall the already installed version permitted by versionlock while targeting only the specified repository. This allows you to verify a package transaction in which repository selection, the module stream, and versionlock are applied at the same time.

Terminal window
sudo dnf --repo "<<REPO_ID>>" reinstall -y "<<MODULE_PACKAGE>>"
sudo rpm -q --qf '%{VERSION}\n' "<<MODULE_PACKAGE>>" | grep -E "^<<MODULE_STREAM>>\."
sudo dnf versionlock list | grep -F "<<MODULE_PACKAGE>>"
module_output="$(sudo dnf module list "<<MODULE_NAME>>")"
printf '%s\n' "$module_output"
printf '%s\n' "$module_output" | grep -E "^<<MODULE_NAME>>[[:space:]]+<<MODULE_STREAM>>[[:space:]]+\[e\]"

Step 12: Confirm that versionlock suppresses updates to newer candidates

Section titled “Step 12: Confirm that versionlock suppresses updates to newer candidates”

Because repoquery can inspect repository candidates independently of versionlock, first confirm that a candidate newer than the installed version exists. Then run check-update and confirm that the target package does not appear as an update candidate while versionlock is active.

Terminal window
arch="$(uname -m)"
latest_vr="$(sudo dnf --repo "<<REPO_ID>>" repoquery --arch "$arch" --latest-limit=1 --qf '%{version}-%{release}' "<<MODULE_PACKAGE>>" | tail -n 1)"
installed_before="$(sudo rpm -q --qf '%{VERSION}-%{RELEASE}\n' "<<MODULE_PACKAGE>>")"
test -n "$latest_vr"
test "$installed_before" != "$latest_vr"
check_output="$(sudo dnf --repo "<<REPO_ID>>" check-update "<<MODULE_PACKAGE>>" 2>&1)"
check_rc=$?
test "$check_rc" -eq 0
if printf '%s\n' "$check_output" | grep -Eq "^<<MODULE_PACKAGE>>\."; then
exit 1
fi
installed_after="$(sudo rpm -q --qf '%{VERSION}-%{RELEASE}\n' "<<MODULE_PACKAGE>>")"
test "$installed_before" = "$installed_after"

Step 13: Remove versionlock and return to normal update behavior

Section titled “Step 13: Remove versionlock and return to normal update behavior”

Delete versionlock and confirm that a newer build in the same repository is detected again by check-update. Then perform a normal DNF update and confirm that the installed version changes to the latest version.

Terminal window
sudo dnf versionlock delete "<<MODULE_PACKAGE>>"
if sudo dnf versionlock list | grep -Fq "<<MODULE_PACKAGE>>"; then
exit 1
fi
check_output="$(sudo dnf --repo "<<REPO_ID>>" check-update "<<MODULE_PACKAGE>>" 2>&1)"
check_rc=$?
test "$check_rc" -eq 100
printf '%s\n' "$check_output"
printf '%s\n' "$check_output" | grep -Eq "^<<MODULE_PACKAGE>>\."
arch="$(uname -m)"
latest_vr="$(sudo dnf --repo "<<REPO_ID>>" repoquery --arch "$arch" --latest-limit=1 --qf '%{version}-%{release}' "<<MODULE_PACKAGE>>" | tail -n 1)"
sudo dnf upgrade -y "<<MODULE_PACKAGE>>"
installed_vr="$(sudo rpm -q --qf '%{VERSION}-%{RELEASE}\n' "<<MODULE_PACKAGE>>")"
test "$installed_vr" = "$latest_vr"

Reset the explicitly enabled stream and confirm that no [e] marker indicating an enabled stream remains for the target module. Resetting the stream does not remove the installed package itself.

Terminal window
sudo dnf module reset -y "<<MODULE_NAME>>"
module_output="$(sudo dnf module list "<<MODULE_NAME>>")"
printf '%s\n' "$module_output"
if printf '%s\n' "$module_output" | grep -Eq "^<<MODULE_NAME>>[[:space:]].*\[e\]"; then
exit 1
fi

Delete the DNF cache and then rebuild the metadata cache. Finally, query the target repository and confirm that package information can be retrieved using the rebuilt metadata.

Terminal window
sudo dnf clean all
sudo dnf makecache
sudo dnf --repo "<<REPO_ID>>" repoquery --disable-modular-filtering --latest-limit=1 "<<MODULE_PACKAGE>>"

To control package sources and installed versions with DNF, it is important to check repositories, module streams, and versionlock separately. Use repoquery to check candidate versions and their sources, select the version series with the module stream, and lock the permitted version with versionlock to prevent unintended updates.

After removing versionlock, the package returns to normal update-candidate behavior. If repository information appears inconsistent or the cache may be outdated, rebuild the metadata with clean all and makecache, then check again.

Category: Linux