You run dnf upgrade and the transaction aborts
You run sudo dnf upgrade and the solver stops at a dependency conflict. A new kernel package pulls in a library version that breaks your proprietary GPU driver. Or maybe you just want to keep your current desktop environment stable while the rest of the system catches up. You need a way to tell DNF to leave specific packages alone without typing a long flag every single time.
Pin the package now. Debugging a broken dependency chain later takes hours.
What DNF actually does with exclusions
DNF reads configuration files in a strict precedence order. The base configuration lives in /etc/dnf/dnf.conf. Fedora also supports a drop-in directory at /etc/dnf/dnf.conf.d/ for modular overrides. When you add an exclude= directive, DNF filters the package list before calculating dependencies. The excluded package stays at its current version, and DNF will refuse to upgrade it or install a newer version unless you explicitly force it.
Think of it like putting a package on a permanent hold in your local repository. DNF still downloads the metadata for the newer version, but it removes the package from the candidate list during transaction resolution. The package manager will still upgrade everything else that does not directly depend on the newer version of the held package.
Config files in /etc/ are user-modified and survive package updates. Files in /usr/lib/ ship with the package and get overwritten on upgrade. Always place your overrides in /etc/dnf/dnf.conf.d/. Never edit /usr/lib/dnf/dnf.conf.
Save the file and run a test update. DNF reads the drop-in directory on every transaction.
How to configure persistent exclusions
Here is how to create a persistent exclusion rule using the modern drop-in directory structure. The directory approach keeps your overrides separate from the base configuration and makes cleanup straightforward.
# Create the drop-in directory if it does not exist yet
sudo mkdir -p /etc/dnf/dnf.conf.d
# Write the exclusion rule to a dedicated configuration file
sudo tee /etc/dnf/dnf.conf.d/excludes.conf > /dev/null << 'EOF'
[main]
# Space-separated list of packages to skip during upgrades
exclude=package-name-1 package-name-2
# Wildcards work but can accidentally block unrelated updates
# exclude=firefox-*
EOF
The [main] section header is required. DNF ignores the exclude directive if it appears outside a valid section. Package names must match the exact repository name. You can verify the exact name by running dnf list installed | grep partial-name. Spaces separate multiple entries. You do not need commas or brackets.
Here is how to apply the configuration and test it against your current repositories.
# Refresh metadata to ensure DNF sees the latest package states
sudo dnf upgrade --refresh
# Check which packages would be updated without committing
sudo dnf check-update
The --refresh flag forces DNF to download fresh metadata from all enabled repositories. This is the normal weekly maintenance command. dnf system-upgrade is for crossing major Fedora releases. They are different commands. Do not conflate them.
Check the update list before committing. If the package still shows up, your syntax is wrong.
Verify the hold is active
DNF provides a built-in configuration dump that shows exactly what the package manager will use during the next transaction. Run the dump command to confirm your exclusion is loaded.
# Print the active configuration values DNF will use
dnf config-manager --dump | grep exclude
# Show the full transaction plan without installing anything
sudo dnf upgrade --downloadonly --assumeno
The --downloadonly --assumeno combination simulates the entire transaction. DNF resolves dependencies, checks for conflicts, and stages the packages in /var/cache/dnf/. If your excluded package does not appear in the staged list, the hold is active. You can safely run the actual upgrade afterward.
Run dnf check-update first. Read the actual candidate list before guessing.
Common pitfalls and what the error looks like
Holding packages works until the rest of the system moves past the held version. DNF will eventually refuse to upgrade other packages because they require a newer version of the held dependency. The transaction test fails and prints a clear conflict message.
Error: Transaction test error:
package kernel-6.8.5-200.fc40.x86_64 requires kernel-modules >= 6.8.5-200.fc40, but none available
package glibc-2.39-22.fc40.x86_64 requires glibc-common >= 2.39-22.fc40, but none available
The conflict is intentional. DNF is telling you that the newer kernel or library cannot install because the held package blocks the required companion package. You must either remove the exclusion or accept that the system will stop updating entirely until you resolve the hold.
Wildcards are another common trap. Writing exclude=python3-* blocks python3-libs, python3-rpm-macros, and every other package that starts with that prefix. DNF does not warn you about the collateral damage until the transaction fails. Stick to exact package names unless you fully understand the repository namespace.
Forgetting to remove the exclusion is the most frequent mistake. A package held for three months becomes a security liability. Fedora's release cadence is six months. The N-2 release goes EOL when N+1 ships. Plan upgrades on that cycle. Remove old exclusions before running a major release upgrade.
Remove the exclusion before the next major release. Held packages accumulate technical debt.
When to use this vs alternatives
Use the exclude= directive in /etc/dnf/dnf.conf.d/ when you want a permanent, system-wide hold on specific packages. Use the --exclude command-line flag when you only need to skip a package for a single update run. Use the dnf-versionlock plugin when you need to lock exact version numbers instead of just package names. Use --disableexcludes=all when you are troubleshooting a broken dependency chain and need to force an upgrade.
Match the tool to the timeline. Temporary skips stay temporary. Permanent holds require maintenance.