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.
Variable notation
Section titled “Variable notation”Values that vary depending on the environment or target package are represented by the following variables.
| Variable name | Example setting | Description |
|---|---|---|
<<REPO_ID>> | appstream | ID of the DNF repository to operate on |
<<MODULE_NAME>> | nodejs | Name of the module to check and enable |
<<MODULE_STREAM>> | 22 | Module stream to enable and use for package installation |
<<MODULE_PACKAGE>> | nodejs | Package to install from the module stream and manage with versionlock |
Step 1: Prepare DNF management plugins
Section titled “Step 1: Prepare DNF management plugins”Prepare config-manager for switching repository settings and versionlock for locking package versions.
sudo dnf install -y dnf-plugins-core python3-dnf-plugin-versionlockStep 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.
sudo dnf repolist --allStep 3: Disable the target repository
Section titled “Step 3: Disable the target repository”Disable the target repository and confirm that it appears in the list of disabled repositories.
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:]]"Step 4: Re-enable the target repository
Section titled “Step 4: Re-enable the target repository”Re-enable the target repository for subsequent package operations and confirm that it has returned to the list of enabled repositories.
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:]]"Step 5: Check available module streams
Section titled “Step 5: Check available module streams”Display the stream list for the target module and confirm that <<MODULE_STREAM>> is available.
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.
sudo dnf module reset -y "<<MODULE_NAME>>"Step 7: Enable the module stream to use
Section titled “Step 7: Enable the module stream to use”Enable <<MODULE_STREAM>> and confirm that the target stream is marked with [e], indicating that it is enabled.
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.
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.
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.
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.
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.
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 0if printf '%s\n' "$check_output" | grep -Eq "^<<MODULE_PACKAGE>>\."; then exit 1fiinstalled_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.
sudo dnf versionlock delete "<<MODULE_PACKAGE>>"if sudo dnf versionlock list | grep -Fq "<<MODULE_PACKAGE>>"; then exit 1ficheck_output="$(sudo dnf --repo "<<REPO_ID>>" check-update "<<MODULE_PACKAGE>>" 2>&1)"check_rc=$?test "$check_rc" -eq 100printf '%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"Step 14: Reset the module stream
Section titled “Step 14: Reset the module stream”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.
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 1fiStep 15: Delete and rebuild the DNF cache
Section titled “Step 15: Delete and rebuild the DNF cache”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.
sudo dnf clean allsudo dnf makecachesudo dnf --repo "<<REPO_ID>>" repoquery --disable-modular-filtering --latest-limit=1 "<<MODULE_PACKAGE>>"Summary
Section titled “Summary”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.