DNF4 vs DNF5: Command Differences and Script Migration Guide

Compare DNF4 and DNF5 command differences, plugins, history syntax, and group handling, and learn what to change in scripts before your distribution moves to DNF5.

Published

Updated

Read time 12 min read

Reviewed byDeepak Prasad

DNF4 versus DNF5 package manager comparison and migration guide

You type dnf install on two RPM-based servers and expect the same behaviour, but the generation underneath is not always identical. DNF4 still powers most RHEL-compatible releases today. DNF5, built on libdnf5, is available on newer Fedora releases and may be adopted more broadly as individual vendors integrate and support it.

This guide focuses on what matters for daily administration and script migration: version detection, renamed flags, history and group syntax, plugin layout, and automation pitfalls. I ran the commands below on DNF 4.20.0 and DNF5 5.4.2.1. When you compare output from two different distributions, repository metadata and package versions can change the result — that is not always a DNF4-versus-DNF5 difference.

Tested on: DNF 4.20.0; DNF5 5.4.2.1.

NOTE
Fedora switched the default dnf command to DNF5 starting with Fedora 41. Many current RHEL-compatible systems still invoke DNF4 by default, although some releases, including Rocky Linux 10, may make DNF5 available separately. Always verify with dnf --version, command -v dnf4, and command -v dnf5 rather than assuming from the distribution name.

DNF4 vs DNF5 at a Glance

DNF4 uses the older DNF and libdnf stack with Python-based plugins in dnf-plugins-core. DNF5 is a reworked implementation on libdnf5 with a cleaner C++ core and a different plugin layout — not a wholesale rename of every familiar subcommand.

Area DNF4 DNF5
Core stack DNF / libdnf libdnf5 / dnf5
Basic install/remove commands Supported Mostly compatible
Download directory option --downloaddir (recent builds also alias --destdir) --destdir only
Disable repositories --disablerepo --disable-repo (--disablerepo retained as a compatibility alias)
Enable extra repository --enablerepo --enable-repo (--enablerepo retained as a compatibility alias)
Transaction history dnf history or dnf history list dnf history list; explicit subcommand required
Group list dnf group list may include environments Use dnf environment list for environments
Script output Mostly human-formatted tables JSON available for several commands
Task DNF4 DNF5
Install package dnf install nginx dnf install nginx
Remove package dnf remove nginx dnf remove nginx
Upgrade packages dnf upgrade dnf upgrade
Search package dnf search nginx dnf search nginx
List repositories dnf repolist dnf repo list (repolist remains a compatibility alias)
Download package dnf download nginx dnf download nginx
Select destination --downloaddir DIR --destdir DIR
View history dnf history dnf history list

Do not treat “uses dnf” as proof of DNF4. On DNF5 systems, /usr/bin/dnf may be a symlink to dnf5.


Check Whether You Are Using DNF4 or DNF5

The command name alone is not enough. Start with the version string, then confirm which binaries and RPMs are installed.

If dnf --version shows 4.x

bash
dnf --version
output
4.20.0
  Installed: dnf-0:4.20.0-22.el10_2.rocky.0.1.noarch at Mon 06 Jul 2026 03:27:42 PM GMT
  Built    : Rocky Linux Build System <[email protected]> at Tue 19 May 2026 02:06:23 PM GMT

  Installed: rpm-0:4.19.1.1-23.el10.x86_64 at Mon 06 Jul 2026 03:27:40 PM GMT

A leading 4.x means you are on DNF4. Check whether a separate dnf5 binary exists:

bash
command -v dnf5
output
(no output — dnf5 not in PATH)

On this system /usr/bin/dnf points at the classic DNF4 entry point:

bash
ls -la /usr/bin/dnf
output
lrwxrwxrwx. 1 root root 5 May 19 05:30 /usr/bin/dnf -> dnf-3
bash
rpm -q dnf dnf5
output
dnf-4.20.0-22.el10_2.rocky.0.1.noarch
package dnf5 is not installed

If dnf --version shows 5.x

bash
dnf --version
output
dnf5 version 5.4.2.1
dnf5 plugin API version 2.0
libdnf5 version 5.4.2.1
libdnf5 plugin API version 2.2

Here dnf and dnf5 are the same tool:

bash
ls -la /usr/bin/dnf /usr/bin/dnf5
output
lrwxrwxrwx. 1 root root       4 May  6 00:00 /usr/bin/dnf -> dnf5
-rwxr-xr-x. 1 root root 1679560 May  6 00:00 /usr/bin/dnf5

You may find only the dnf5 RPM installed:

bash
rpm -q dnf dnf5
output
package dnf is not installed
dnf5-5.4.2.1-1.fc44.x86_64

On releases that package both during migration, compare dnf4 --version and dnf5 --version on the same host to isolate generation differences from repository metadata.


Command-Line and Option Differences

Install, remove, upgrade, search, repolist, and repoquery still feel familiar on both generations. The breakage usually appears in flags, help syntax, plugins, and command output.

Download and destination directory options

On DNF4, dnf download often accepts both directory flag names:

bash
dnf download --help | grep -E 'destdir|downloaddir'
output
--destdir DESTDIR, --downloaddir DESTDIR
bash
dnf download --resolve --alldeps --downloaddir=/tmp/dnf4-mig htop
output
(30/30): tzdata-2026b-1.el10_2.noarch.rpm       513 kB/s | 497 kB     00:00

On DNF5, help shows only --destdir:

bash
dnf download --help | grep destdir
output
--destdir=DESTDIR                        Set directory used for downloading packages to.

Passing the DNF4 flag on DNF5 fails immediately:

bash
dnf download --downloaddir=/tmp/dnf5-test2 htop
output
Unknown argument "--downloaddir=/tmp/dnf5-test2" for command "download". Add "--help" for more information about the arguments.

See download RPM packages with all dependencies for the full offline-download workflow.

Repository enable and disable flags

DNF4 documents --disablerepo and --enablerepo in global help:

bash
dnf --help | grep -i disablerepo | head -1
output
--disablerepo [repo]  Temporarily disable active repositories for the

DNF5 uses --disable-repo and --enable-repo as the canonical names. The older DNF4 spellings are currently retained as compatibility aliases, but scripts should migrate to the canonical DNF5 forms rather than depend on aliases remaining indefinitely:

bash
dnf --help | grep -i disable-repo | head -1
output
--disable-repo=REPO_ID,...             Disable repositories. List option. Supports globs, can be specified multiple times.

DNF5 also adds --from-repo for restricting the requested package to one repository while resolving dependencies from other enabled repos. DNF4 does not provide an exact equivalent; restricting --repo applies to the entire transaction.

Help syntax changes

DNF4 still supports --help-cmd on subcommands such as download:

bash
dnf download --help-cmd 2>&1 | head -1
output
usage: dnf download [-c [config file]] [-q] [-v] [--version]

DNF5 rejects it — use --help instead:

bash
dnf download --help-cmd 2>&1 | head -1
output
Unknown argument "--help-cmd" for command "download". Add "--help" for more information about the arguments.

Group and Environment Command Differences

Group IDs and display names come from each distribution’s Comps repository metadata. Fedora and Rocky Linux may expose different groups even when both use the same DNF generation, so do not treat cross-distribution output as proof of DNF4-versus-DNF5 behaviour.

Both generations accept group specifications by name or ID where your release supports them:

bash
dnf group install "Development Tools"
dnf group install development-tools

When using the general install command, prefix the group with @:

bash
dnf install @development-tools

The @ prefix belongs on commands that accept both package and group specifications. dnf group install already expects a group name or ID — do not pass @development-tools to dnf group install.

DNF4 DNF5
dnf group list --ids dnf group list always shows IDs
dnf group list may include environments Use dnf environment list for environments
dnf groupinstall GROUP alias Use dnf group install GROUP
dnf groupinfo GROUP alias Use dnf group info GROUP
dnf group mark install GROUP Use dnf group install --no-packages GROUP

On DNF5, environments are listed separately:

bash
dnf environment list
output
ID                                Name                                 Installed
custom-environment                Fedora Custom Operating System              no
workstation-product-environment   Fedora Workstation                          no

Group IDs may still differ across Fedora, RHEL, Rocky Linux, and other distributions because each repository publishes its own Comps metadata. Discover the ID on the target system with dnf group list rather than hard-coding one value in scripts.


Plugin and config-manager Differences

Not every DNF5 feature is a plugin. Official documentation classifies download, history, system-upgrade, and offline workflows as core DNF5 commands, while config-manager, needs-restarting, and reposync are plugins.

Feature DNF4 DNF5
Download dnf-plugins-core Core DNF5 command
History Core DNF4 command Core DNF5 command; subcommand required
Config manager Usually dnf-plugins-core DNF5 plugin
Needs restarting Usually dnf-plugins-core DNF5 plugin
Reposync dnf-plugins-core DNF5 plugin
System upgrade Separate plugin/package on supported distributions Core DNF5 command
Offline transactions Distribution/plugin-specific workflow Core --offline and dnf offline workflow

On a typical DNF4 install you will see dnf-plugins-core:

bash
rpm -qa | grep -iE 'dnf-plugin|dnf5' | sort
output
dnf-plugins-core-4.7.0-10.el10.noarch
python3-dnf-plugins-core-4.7.0-10.el10.noarch

On DNF5 the stack is packaged differently:

output
dnf5-5.4.2.1-1.fc44.x86_64
dnf5-plugins-5.4.2.1-1.fc44.x86_64
libdnf5-5.4.2.1-1.fc44.x86_64
libdnf5-cli-5.4.2.1-1.fc44.x86_64

config-manager syntax changes

DNF4 often adds a repository with an option:

bash
sudo dnf config-manager --add-repo https://example.com/example.repo

DNF5 uses the addrepo subcommand:

bash
sudo dnf config-manager addrepo --from-repofile=https://example.com/example.repo

To create a repository from a base URL with DNF5:

bash
sudo dnf config-manager addrepo --id=example --set=baseurl=https://example.com/packages/

DNF5 also uses explicit subcommands for persistent repository state:

bash
sudo dnf config-manager disable example
sudo dnf config-manager enable example

These commands create repository overrides rather than directly editing the original .repo file.


Transaction History and System State

DNF4 and DNF5 both provide transaction history, but they use separate history and system-state storage. They share the RPM database, yet DNF4 transaction records are not copied into the DNF5 history database during migration.

On DNF4, the bare command defaults to listing transactions:

bash
dnf history
output
5 | install -y epel-release  | 2026-07-06 22:35 | Install        |    1 >E
     4 | install -y libwebp       | 2026-07-06 22:30 | Install        |    1
     3 | install -y tar gzip      | 2026-07-06 22:02 | Install        |    1
     2 | -y install git           | 2026-07-06 21:09 | Install        |   65  <
     1 |                          | 2026-07-06 20:56 | Install        |  307 >E

The explicit form returns the same list:

bash
dnf history list

DNF5 requires a subcommand. A bare dnf history returns:

output
Missing command. Add "--help" for more information about the arguments.

Use dnf history list instead:

bash
dnf history list
output
ID Command line                          Date and time       Action(s) Altered
 2 dnf5 --config /kiwi_dnf5.config -y -- 2026-07-09 06:47:24                 1
 1 dnf5 --config /builddir/result/image/ 2026-07-09 06:47:17               147

Other DNF5 history operations include:

bash
dnf history info last
dnf history undo last
dnf history redo last
dnf history rollback TRANSACTION_ID

A bare dnf history on DNF5 is a command-syntax difference, not missing history support.

During a supported migration, DNF5 can migrate system-state information such as package install reasons, but older DNF4 transaction history is not carried forward. Continued use of both tools can also make packages installed by one appear user-installed to the other. Do not manually merge history databases.


Migrate Scripts and Automation to DNF5

Review automation any time the underlying DNF generation changes on systems you manage.

Update renamed options

DNF4 DNF5
dnf download --downloaddir=DIR dnf download --destdir=DIR
dnf --disablerepo='*' install … dnf --disable-repo='*' install …
dnf repolist dnf repo list (repolist is retained as an alias)
dnf download --help-cmd dnf download --help

Update history and group commands

Replace bare dnf history with dnf history list on DNF5. Replace deprecated group aliases (groupinstall, groupinfo, grouplist) with dnf group install, dnf group info, and dnf group list. Query environments with dnf environment list instead of expecting them in dnf group list.

Update config-manager commands

Move from option-style dnf config-manager --add-repo … to subcommand-style dnf config-manager addrepo …, and use dnf config-manager enable / disable for persistent repository state. See the examples in the plugin section above.

Avoid parsing human-readable output

dnf repoquery --info still prints human-formatted text. DNF5 provides JSON for several commands — prefer that in scripts when it is available:

bash
dnf list --json nginx
output
{
  "available_packages":[
    {
bash
dnf history list --json
output
[
  {
    "id":2,
    "command_line":"dnf5 --config \/kiwi_dnf5.config -y --disable-plugin=priorities,versionlock --releasever=44 --setopt=clean_requirements_on_remove=true remove systemd-standalone-tmpfiles",
    "start_time":1783579644,
bash
dnf history info last --json
dnf needs-restarting --json

Not every DNF command offers JSON output, so scripts should also validate documented exit codes and avoid relying on column spacing, progress bars, or translated status messages. For example, dnf check-upgrade normally exits 100 when updates exist and 0 when none are available — do not treat 100 as a generic failure.

Ansible users should verify which package backend their managed hosts require. Current ansible-core provides both ansible.builtin.dnf and ansible.builtin.dnf5. The DNF5 module requires libdnf5 Python bindings, and some parameters may differ from the older DNF module, so test existing playbooks against the target ansible-core and distribution versions.

For a broader command reference, see the DNF command cheat sheet.


Should You Replace DNF4 with DNF5 Manually?

Generally no — use the package manager your distribution ships and supports.

Situation Recommendation
Distribution already on DNF5 Use dnf as documented for that release
Distribution still on DNF4 Stay on DNF4 until your vendor documents a supported migration
Pre-migration testing Use a VM or container with the target generation, not unsupported package swaps on production
Both binaries installed Possible during transitions, but not for routine mixed use

A DNF5 package being available does not mean the distribution has adopted it as the supported default or recommends replacing DNF4. Installing DNF5 alongside DNF4 may be useful for testing when the vendor packages both, but follow the distribution's documented migration path before changing production package-management workflows.


Common Problems and Best Practices

Symptom Likely cause Fix
Unknown argument --downloaddir You are on DNF5 Use --destdir
Unknown argument --help-cmd DNF5 subcommand Use --help
Missing command for bare dnf history DNF5 requires a subcommand Use dnf history list
Module or Group '@…' is not available with dnf group install @ syntax used on the wrong command Use dnf install @group or dnf group install group-id
No match for argument: Development Tools Group name is absent from the target's Comps metadata, or a name containing spaces was not quoted Run dnf group list; use the listed ID or quote the exact display name
Script parses old DNF4 table output DNF5 changed formatting Use --json where available, or repoquery / rpm -q
Packages show odd install reasons after switching tools Separate DNF4/DNF5 state Use one supported tool per system
dnf check-upgrade exits 100 Updates are available Treat exit 100 as updates present, not script failure
Manually forcing DNF5 before vendor support Unsupported or incomplete packaging Follow vendor guidance; test in VMs or containers

Run dnf --version in playbooks and documentation prerequisites. Pin flag names per generation in scripts. Retest group installs, offline installs, and download bundles after a DNF generation change.


Summary

Most day-to-day package operations still start with dnf install, dnf remove, and dnf upgrade. The practical migration work is in flags (--downloaddir vs --destdir, --disablerepo vs --disable-repo), history syntax (dnf history vs dnf history list), group and environment commands, config-manager subcommands, plugin layout, and separate transaction-history databases.

Before you update scripts or tutorials, run dnf --version on each system you manage, verify which plugins and JSON output options exist there, and exercise your workflows on the DNF generation your users actually run. Follow your distribution’s supported default rather than manually replacing the system package manager.


Frequently Asked Questions

1. How do I tell if I am running DNF4 or DNF5?

Run dnf --version. A leading 4.x means DNF4; 5.x or dnf5 version output means DNF5. Also check command -v dnf5 and whether /usr/bin/dnf is a symlink to dnf5, because some systems invoke DNF5 through the normal dnf command.

2. Is DNF5 a drop-in replacement for DNF4?

Core install, remove, upgrade, search, and repolist commands are largely familiar, but some flags, plugins, group syntax, help options, and history command forms differ. Retest scripts and tutorials rather than assuming identical behaviour.

3. Can I install DNF5 alongside DNF4 on the same system?

Some distributions may package both during a transition, but running them interchangeably for daily package management is not recommended. Transaction histories and package-state metadata are separate, and mixing the two tools can produce confusing install reasons and group state.

4. Should I use --downloaddir or --destdir?

Use --downloaddir on DNF4. Use --destdir on DNF5. Recent DNF4 builds may accept --destdir as an alias, but DNF5 rejects --downloaddir.

5. Why does dnf history return Missing command on DNF5?

DNF5 requires an explicit history subcommand. Use dnf history list to view transactions, or use info, undo, redo, rollback, and store for other operations. A bare dnf history command returns Missing command because optional subcommands are no longer supported; transaction history itself is available.
Omer Cakmak

Linux Administrator

Highly skilled at managing Debian, Ubuntu, CentOS, Oracle Linux, and Red Hat servers. Proficient in bash scripting, Ansible, and AWX central server management, he handles server operations on …