Packages and updates: installing, upgrading and knowing what changed
How dnf and apt resolve packages, and how to inspect or undo what an update changed.

A server’s software is not one program but thousands of packages — bundles of files with version metadata and a list of what they depend on — and the package manager is what turns a request like “install nginx” into a transaction: read the repository index, resolve dependencies, download, install, and record what happened. The install is the easy part. The reason updates deserve a sheet of their own is the aftermath: an upgrade can replace a library that a running service loaded at start-up, drop a new default configuration file next to the one you edited, or pull in software you never asked for, and the machine keeps running as if nothing had changed. Knowing what changed is the skill.
What a package manager is doing
A repository publishes a signed index of packages and their dependency information. On RHEL 9, dnf reads repository definitions under /etc/yum.repos.d/ and its own settings from /etc/dnf/dnf.conf; on Ubuntu, apt reads sources from /etc/apt/sources.list.d/, which on 24.04 LTS is the deb822 file /etc/apt/sources.list.d/ubuntu.sources. Installation is a dependency solve, not a copy: the manager chooses a set of package versions that satisfies every requirement at once, which is why a single request can add or change dozens of packages and why “it only updates one thing” is rarely true.
Refresh the index, then act
Two verbs are involved, and keeping them apart makes the change inspectable. First the local view of the repositories is refreshed, then the upgrades are applied. apt update refreshes the index; apt upgrade installs available upgrades and never removes an installed package. dnf can do both in one invocation, or you can separate them with dnf check-update and look before committing.
A security update is an ordinary package update whose advisory says it fixes a vulnerability — not a different kind of object. RHEL tags these advisories, so dnf upgrade --security installs only packages that fix one and dnf updateinfo lists them. Ubuntu ships security updates automatically through unattended-upgrades, whose allowed origins live in /etc/apt/apt.conf.d/50unattended-upgrades and default to the official Ubuntu repositories, the -security suite among them.
| Task | dnf (RHEL 9) | apt (Ubuntu) |
|---|---|---|
| Refresh the index | dnf check-update |
apt update |
| Apply upgrades | dnf upgrade |
apt upgrade |
| Security fixes only | dnf upgrade --security |
security origins in 50unattended-upgrades |
| See what changed | dnf history list, dnf history info <id> |
/var/log/dpkg.log |
| Keep a package from being upgraded | dnf upgrade --exclude=<pkg> (that run only) |
apt-mark hold (until unhold) |
| Undo a transaction | dnf history undo <id> |
reinstall pkg=version |
Knowing what changed, and undoing it
dnf keeps a numbered history of what each transaction did: dnf history list shows the transactions and dnf history info <id> expands one into every install, upgrade and erase. dnf history undo <id> performs the opposite of that transaction, and dnf history rollback <id> undoes everything after it. apt has no equivalent single undo: every package action is logged in /var/log/dpkg.log, a package can be pinned with apt-mark hold, and one package can be reinstalled at an exact version with apt install pkg=version. That asymmetry is worth internalising — on Debian-family systems, rollback is a decision you make per package, deliberately, before you press enter.
Why a silent update changes behaviour
Three mechanisms explain how an update that printed “Complete!” can still alter the system. First, a running process keeps the code it loaded at start, so updating a shared library such as glibc or openssl-libs changes nothing for a daemon until that daemon restarts; the fix is on disk but not in the running service. Red Hat documents which packages make a reboot advisable and ships dnf needs-restarting to report it. Second, a package can ship a new default configuration file beside the one you edited: RPM replaces neither version silently, and which of the two the service goes on reading depends on how the package marked that file — your version saved aside as .rpmsave while the shipped one takes over, or your version kept in place while the shipped default arrives as .rpmnew. Third, unattended upgrades can reboot the host themselves when a kernel update requires it. None of these is a fault; all three are reasons to read what a transaction touched.
Limits and the common error
A security update keeps the same major version and closes a defect; a release upgrade — dnf system-upgrade or Ubuntu’s do-release-upgrade — moves the system to a new distribution version and is a change of platform, not a patch. Treating the second as the first is the classic mistake. Two more: apt full-upgrade may remove packages to complete an upgrade where apt upgrade will not, so the two commands are not interchangeable; and adding an unofficial repository to get one package widens what every later upgrade may install — on Ubuntu, packages from universe and multiverse do not receive the same security maintenance as those from main.
Level and prerequisites. L2 — operational. It assumes the L1 fundamentals of the Linux filesystem and of users and permissions, and it names systemd only as the thing that restarts a service after an update.
Where to go next
- Server & Virtualization — the area this sheet belongs to.
- Operating systems — the node this sheet sits under.
- Knowledge — the index of every published sheet.
References
- dnf(8) — DNF Command Reference: https://man7.org/linux/man-pages/man8/dnf.8.html — supports
--security, the transaction history,history undo/rollback, and the RPM configuration-file replacement policy. - dnf.conf(5) — DNF Configuration Reference: https://man7.org/linux/man-pages/man5/dnf.conf.5.html — supports the repository layout and the
installonlypkgsrule that kernels are never upgraded in place. - apt(8) — Debian bookworm manual: https://manpages.debian.org/bookworm/apt/apt.8.en.html — supports
update/upgrade/full-upgradesemantics andinstall pkg=version. - apt-mark(8) — Debian bookworm manual: https://manpages.debian.org/bookworm/apt/apt-mark.8.en.html — supports
hold/unholdand the automatic/manual distinction. - Ubuntu Server documentation — Install and manage packages: https://ubuntu.com/server/docs/package-management — supports apt sources,
dpkg -l/-L/-Sand the/var/log/dpkg.loglog. - Ubuntu Server documentation — Automatic updates: https://ubuntu.com/server/docs/how-to/software/automatic-updates/ — supports
unattended-upgrades, the security origins and the possible automatic reboot. - Ubuntu documentation — Security updates: https://documentation.ubuntu.com/security/security-updates — supports the difference in security maintenance between
mainanduniverse/multiverse. - Ubuntu Server documentation — How to upgrade your Ubuntu release: https://documentation.ubuntu.com/server/how-to/software/upgrade-your-release/ — supports
do-release-upgradeas the release-upgrade counterpart. - unattended-upgrade(8) — Debian bookworm manual: https://manpages.debian.org/bookworm/unattended-upgrades/unattended-upgrade.8.en.html — supports the log locations.
- Red Hat — Identify packages that require a reboot after an update: https://access.redhat.com/solutions/27943 — supports which packages make a reboot advisable and why a running service keeps the older library until it is restarted.
- openSUSE Leap Reference — Managing software with command line tools: https://doc.opensuse.org/documentation/leap/reference/html/book-reference/cha-sw-cl.html — supports the two configuration-file outcomes of an upgrade (
.rpmsave,.rpmnew); added during the editorial review. - dnf5-needs-restarting(8) — Arch manual pages: https://man.archlinux.org/man/extra/dnf5/dnf5-needs-restarting.8.en — supports the
needs-restartingbehaviour and exit codes.