You need a complete inventory of what is on the system
You just cloned a virtual machine, or you are auditing a production server, or you finally decided to clean up a desktop that has been running for three years. The terminal is open. You need a reliable way to query the package database, understand what the output means, and export it without corrupting the data. You type dnf list installed and get a wall of text. You try to pipe it to grep and the formatting breaks. This guide covers the exact commands you need, why they work the way they do, and how to avoid the common traps that break package queries.
How the package database actually works
Fedora does not keep a simple text file of installed software. The system uses an RPM database backed by SQLite. Every time you install, upgrade, or remove a package, DNF updates that database and writes a transaction record. The database tracks package names, versions, release tags, architecture, install dates, and file ownership. When you run a query, DNF reads the local SQLite database, applies your filters, and formats the result.
Think of the RPM database like a warehouse ledger. The packages are the boxes on the shelves. The ledger records what box arrived, when it arrived, what it contains, and which supplier delivered it. You never edit the ledger by hand. The warehouse manager handles the updates. If the ledger gets corrupted, you run a rebuild command to reconstruct it from the actual files on disk. Treat the database as the single source of truth. Manual file edits drift. The database stays consistent.
The database lives in /var/lib/rpm/. You never touch those files directly. DNF and RPM maintain them automatically. If your queries return stale results, the issue is almost always a cached metadata mismatch, not a broken database. Clear the cache before you assume the system is misconfigured.
Query the database with dnf
List everything currently installed
Here is how to pull a complete inventory of every package the system knows about.
dnf list installed
# Reads the local RPM SQLite database
# Formats output as name.arch version repository
# @System means the package was installed locally or via a previous transaction
The output shows three columns. The first column is the package name and architecture. The second column is the version and release tag. The third column shows the repository source. Packages that came from a repository show the repo name. Packages that were installed from a local RPM file or carried over from an upgrade show @System. This marker tells you the package is not currently tied to a remote repository stream.
Run dnf list installed before any major system change. Future-you will need a baseline.
Filter by name or pattern
Here is how to narrow the output to a specific package family or wildcard pattern.
dnf list installed 'python3*'
# Uses DNF's native pattern matching
# Avoids piping to grep which breaks column alignment
# Returns only packages matching the exact prefix
Piping dnf list installed to grep is a common habit that causes problems. DNF formats output with dynamic column widths. When you pipe to grep, you lose the repository column context and you risk matching version strings instead of package names. DNF's built-in glob matching is safer and faster. It queries the database directly instead of scanning formatted text.
If you need to search across multiple terms, chain the patterns with commas. DNF handles the union internally.
Inspect a single package
Here is how to pull detailed metadata for one specific package.
dnf info firefox
# Queries the database for a single package
# Shows installed version, available version, and repo
# Displays description, license, and install size
The info command is your first stop when troubleshooting version mismatches. It shows both the installed version and the latest available version in the enabled repositories. If the installed version is older than the available version, you have pending updates. If the available version is missing, your repository metadata is stale or the package was removed from the stream.
Check dnf info before forcing an install. Forcing over a newer version breaks dependency chains.
Find the package that owns a binary
Here is how to trace a file back to the package that installed it.
dnf provides /usr/bin/ssh
# Searches the file ownership database
# Returns the package name and version
# Works for binaries, config files, and libraries
The provides command queries the RPM file database. It maps every file on the system back to its owning package. This is essential when you find a stray binary or a missing configuration file and need to know which package manages it. You can also use rpm -qf /path/to/file for the same result. Both commands read the same underlying database.
Use provides before deleting files in /usr/. Deleting a managed file breaks the package database and triggers verification failures.
Review transaction history
Here is how to see what changed and when.
dnf history
# Lists all DNF transactions with IDs
# Shows install, upgrade, and erase operations
# Timestamps help you correlate changes with incidents
Every DNF operation gets a transaction ID. You can use that ID to inspect exactly what changed, roll back the operation, or undo it. The history log is stored in /var/lib/dnf/history.sqlite. It survives reboots and survives minor database corruption. If you broke something during an upgrade, the history log is your rollback path.
Run dnf history after any system change. Keep a mental note of the transaction ID before you reboot.
Verify the output matches reality
Here is how to cross-check the package list against the actual files on disk.
rpm -Va
# Verifies all installed packages
# Compares file sizes, permissions, and checksums
# Returns empty output when everything matches
The verification command compares every installed file against the RPM database records. It checks file size, permissions, ownership, modification time, and cryptographic checksums. A clean run produces no output. Any line that appears means a file has been modified outside the package manager. This is normal for configuration files in /etc/. It is a warning sign for binaries in /usr/bin/.
Run rpm -Va after a suspected intrusion or a botched manual edit. Trust the package manager. Manual file edits drift, snapshots stay.
Common pitfalls and broken queries
The dnf list installed command will refuse to proceed and print Error: Failed to download metadata for repo 'fedora' when your network is down or your repository cache is stale. The fix is almost always dnf clean all followed by dnf upgrade --refresh. The --refresh flag forces DNF to discard cached metadata and fetch fresh copies from the mirrors. This is the normal weekly maintenance command. Do not confuse it with dnf system-upgrade, which is reserved for crossing major Fedora releases.
Another frequent trap is assuming @System means a package is unmanaged. It does not. It only means the package was not installed from a currently enabled repository. Packages carried over from previous Fedora releases, or packages installed from local RPM files, get this marker. They are still tracked by the RPM database. They still receive updates if you enable the correct repository stream.
Piping DNF output to wc -l to count packages is unreliable. The command prints a header line and sometimes wraps long lines. Use dnf list installed | grep -c '\.' for a rough count, or rely on rpm -qa | wc -l for an exact count. The RPM command outputs one package per line with no headers. It is faster and more predictable for scripting.
Never edit files in /usr/lib/ to fix a missing configuration. Those files ship with the package. Edit /etc/ instead. The system is designed to override package defaults with user modifications. If you modify /usr/lib/, the next package update will overwrite your changes and leave you debugging a silent regression.
Choose the right tool for the job
Use dnf list installed when you need a clean, formatted inventory with repository context. Use rpm -qa when you are writing scripts that require one package per line and zero headers. Use dnf provides when you need to trace a file back to its owning package. Use dnf history when you need to audit changes or roll back a broken transaction. Use dnf info when you need to compare installed versions against available updates. Use rpm -Va when you need to verify file integrity against the database. Stay on dnf for daily operations. Fall back to rpm only when network access is unavailable or when you need raw database output.