How to List Installed Packages on Fedora with DNF

Use `dnf list installed` to display all packages currently installed on your Fedora system, or pipe the output through `grep` to filter for specific software.

The scenario

You just finished a major system upgrade or you are auditing a server before migrating it. You need a complete inventory of every package currently on the disk. You type a command, get a wall of text, and realize you have no idea which packages came from official repositories, which were pulled in as dependencies, and which you manually installed. The terminal output is accurate, but it is not immediately useful. You need a reliable way to query the system state without guessing.

How the RPM database tracks your software

Fedora uses RPM as its underlying package database. Every time you install, update, or remove software, RPM records the transaction in a SQLite database located at /var/lib/rpm/. DNF sits on top of that database. It does not scan your filesystem for binaries. It reads the transaction history and the current state of the RPM database. When you ask for a list of installed packages, you are asking DNF to query that database and format the results.

The database tracks the package name, version, release, architecture, and the repository it came from. It also tracks installation time and whether the package was explicitly requested or pulled in as a dependency. This distinction matters when you are troubleshooting or cleaning up a system. DNF maintains a separate history log that records every transaction. You can review it later to see exactly what changed and why.

Run dnf history before you start auditing. Knowing the transaction timeline saves hours of guessing later.

The standard inventory command

The default way to see what is on your system is dnf list installed. This command queries the RPM database directly and returns a formatted table. It does not require root privileges because it only reads data. It is the most accurate reflection of your current system state.

Here is how to run the base command and understand the output columns.

# Query the RPM database for all currently installed packages
# The command reads /var/lib/rpm/ and formats the results into a table
dnf list installed

The output shows four columns. The first column is the package name. The second column is the version and release string. The third column is the architecture, usually x86_64 or noarch. The fourth column is the repository source, such as @System, fedora, or updates. The @System tag means the package was installed directly on this machine, not pulled from a remote repository during the current query.

If you are working on a system with hundreds of packages, the terminal buffer will scroll past quickly. Pipe the output to a pager to navigate it comfortably.

# Send the full package list to less for interactive scrolling
# The -S flag preserves case sensitivity while searching
dnf list installed | less

Save the output to a file if you need an audit trail. Redirecting to a text file is safer than copying and pasting from the terminal.

# Write the complete inventory to a timestamped file
# The date command prevents overwriting previous backups
dnf list installed > ~/package-inventory-$(date +%Y%m%d).txt

Check the file size before you archive it. A minimal server might list two hundred packages. A fully loaded workstation can list over two thousand.

Filtering and formatting for real work

Raw lists are rarely what you actually need. You usually want to find a specific package, check for a particular version, or extract just the names for a script. Piping to grep works for simple searches, but it matches against the entire line. That means it will catch version strings and repository names too.

Here is how to filter for a specific package name safely.

# Search the installed list for packages containing the word firefox
# The -i flag ignores case, and the -w flag matches whole words only
dnf list installed | grep -iw firefox

When you need precise control over the output format, dnf repoquery is the right tool. It uses RPM's query format strings to extract exactly what you want. It is slower than grep because it parses the database schema, but it gives you structured output that scripts can parse reliably.

Here is how to extract a clean list of package names and versions.

# Query the installed database with a custom format string
# %{NAME} pulls the package name, %{VERSION} pulls the version
# The \n adds a newline after each entry for clean output
dnf repoquery --installed --queryformat "%{NAME}-%{VERSION}\n"

You can combine this with other filters. The --whatprovides flag shows which package owns a specific file. The --requires flag shows dependencies. These flags turn a simple inventory command into a dependency graph tool.

Do not use yum for these queries. The yum command still exists as a compatibility shim, but it routes everything through DNF anyway. Using dnf directly avoids an extra translation layer and gives you access to modern query flags.

Verifying a specific package

Listing everything is useful for audits. Checking a single package is useful for troubleshooting. When a service fails or a binary is missing, you need to know exactly which package owns it and whether it is actually installed.

Here is how to verify a package and see its detailed metadata.

# Fetch detailed information about a single package
# The info command shows description, license, and install size
dnf info firefox

The output confirms the installed version, the repository it came from, and whether updates are available. If the package is not installed, DNF will show the available version from the repositories instead. Look for the Installed tag in the output. If it is missing, the package is not on your system.

When you suspect a package was partially removed or corrupted, check the RPM database directly. The rpm -V command verifies file checksums, permissions, and ownership against the original package manifest.

# Verify the integrity of a specific package against the RPM database
# The -V flag checks size, md5, permissions, and modification times
rpm -V firefox

A clean run produces no output. Any printed lines indicate a mismatch. A missing binary or a modified configuration file will show up here before it breaks your workflow.

Run the verification before you reinstall. Reinstalling over a working package wastes bandwidth and masks the real problem.

Common pitfalls and database integrity

The RPM database is robust, but it can lock up or become inconsistent. If you interrupt a dnf transaction with Ctrl+C, the database lock file at /var/lib/rpm/.rpm.lock may remain. Subsequent commands will hang or fail with a permission error.

Here is how to clear a stale lock and rebuild the database if queries fail.

# Remove the stale lock file that blocks new transactions
# Only run this if no other dnf or rpm process is active
sudo rm -f /var/lib/rpm/.rpm.lock

# Rebuild the RPM database from the installed package headers
# This recreates the SQLite indexes without touching actual files
sudo rpm --rebuilddb

Database corruption usually shows up as Error: rpmdb: BDB0113 Thread/process or sqlite3.OperationalError: database is locked. The rebuild command fixes the index. It does not reinstall packages. It only reconstructs the lookup tables.

Another common issue is confusing @System with @updates. The @System tag means the package was installed on this host. The @updates tag means it came from the updates repository. Both are installed. The tag only tells you where DNF last fetched the metadata from. Do not assume @System means "manually installed." It means "present on disk."

Check the transaction history to see what was actually requested by the user. DNF tracks explicit installs versus dependency pulls in dnf history info <transaction-id>.

Reboot after a database rebuild if services are misbehaving. Some daemons cache package metadata at startup and will not see the corrected state until they restart.

When to use which tool

Use dnf list installed when you need a quick, human-readable inventory of everything on the system. Use dnf repoquery --installed when you need structured output for scripts or custom formatting. Use rpm -qa when you are inside a minimal container where DNF is not present. Use dnf history when you need to trace who installed a package and when. Use rpm -V when you suspect file corruption or unauthorized modification. Use dnf info when you need to verify a single package before troubleshooting.

Where to go next