How package management works¶
A Linux system is assembled from thousands of separately developed programs and libraries.
Each one has its own release schedule, its own build system and its own idea of where files belong.
Installing them by hand, by downloading a tarball, running ./configure && make install and copying files into /usr/local, works for one program on one machine.
It doesn't tell you later which files belong to which program, which library versions a program was built against, or what to remove when you no longer need it.
A package manager answers those questions. It treats software as packages: named, versioned units with a declared list of files and a declared set of relationships with other packages. It keeps a database of what is installed, fetches new packages from repositories it trusts, works out what else has to change when you ask for one change, and applies the result as a single operation that it can record and, to a degree, reverse.
Every distribution supported here does all of this, but each family does it with different tools, built at different times around different priorities.
Packages: files plus metadata¶
A package is an archive of files together with metadata that describes them.
| Family | Package format | Low-level tool | Installed-package database |
|---|---|---|---|
| Debian/Ubuntu | .deb |
dpkg |
/var/lib/dpkg/ |
| RHEL/Alma/Rocky | .rpm |
rpm |
/usr/lib/sysimage/rpm/ (an SQLite database) |
| Arch | .pkg.tar.zst |
pacman |
/var/lib/pacman/local/ |
| Gentoo | ebuild (a build recipe), or a binary package (.gpkg.tar) |
Portage (emerge) |
/var/db/pkg/ |
The metadata is what makes a package more than a tarball. It typically records:
- the package's name, version and architecture, and the version of the packaging itself (Debian's
-1, the RPM release-8.el10, Arch'spkgrel), so that a rebuild of the same upstream version can still be ordered as newer; - relationships: what it depends on, what it conflicts with, what it replaces, and what abstract capabilities it provides (a mail transfer agent, a shared library with a particular soname);
- the list of files it owns, with checksums, so the system can later answer "which package owns this file?" and "has this file been modified?";
- maintainer scripts or hooks that run before or after files are installed or removed, for example to create a system user or reload a service;
- which files are configuration files, which must not be silently overwritten on upgrade.
The low-level tool (dpkg, rpm) installs, removes and queries individual package files and keeps the database.
It checks that dependencies are satisfied, but doesn't fetch anything: give dpkg -i a package whose dependencies are missing and it stops, leaving the package unconfigured.
Fetching and choosing are the job of the high-level tool (apt, dnf), which reads repository metadata, decides which packages to download and then drives the low-level tool.
pacman combines both roles in one program.
Portage is different again, because its repository contains recipes rather than finished packages: see Binary and source distributions.
Repositories and their metadata¶
A repository is a collection of packages plus an index describing them. The index is what the package manager downloads when you "refresh" or "update the package lists": a compressed list of every package in the repository with its version, dependencies, size and checksum. Searching, dependency resolution and "what would an upgrade change?" all run against this local copy of the index, which is why they're fast and why they can be out of date until you refresh.
The repositories a system uses, and in what order of preference, are configuration.
On Debian and Ubuntu they're entries in /etc/apt/sources.list.d/; on the RHEL family, .repo files in /etc/yum.repos.d/; on Arch, sections of /etc/pacman.conf; on Gentoo, /etc/portage/repos.conf for ebuild repositories and /etc/portage/binrepos.conf for binary package hosts.
The repository configuration reference lists the files and fields.
Distributions split their packages across several repositories on purpose.
Debian separates main, contrib, non-free and (since Debian 12) non-free-firmware by licence, and publishes security updates and stable updates as separate suites from the release itself.
The RHEL family separates BaseOS (the core operating system) from AppStream (applications and runtimes) and CRB (build dependencies).
Arch separates core and extra from the *-testing repositories, which are off by default.
These splits let an administrator choose a trade-off per repository, such as staying with the release while still taking security fixes.
Signatures and the chain of trust¶
Installing a package means running its maintainer scripts as root and placing its files in system directories. A package manager must therefore be sure that a package really comes from the distribution and hasn't been altered on a mirror or in transit. Mirrors are run by volunteers and third parties, so HTTPS alone doesn't establish this: it protects the connection to the mirror, not the content the mirror serves. All four families instead use OpenPGP signatures made with keys the distribution controls, checked against public keys installed on your system.
They sign at different levels:
- Debian and Ubuntu sign the index.
Each repository publishes a
Releasefile listing the checksums of its index files, signed inline asInRelease(or with a detachedRelease.gpg). Each index file lists the checksum of every package. Verifying one signature overInReleasetherefore covers every package through a chain of checksums; the.debfiles themselves aren't normally signed.aptrefuses to use a repository whose signature it can't verify. - The RHEL family signs each package.
Every
.rpmcarries a signature in its header, whichdnfchecks before installation whengpgcheck=1. The repository's index (repomd.xml) can be signed too, whichdnfchecks only whenrepo_gpgcheck=1; the distributions' own repositories don't enable it. - Arch signs each package, with a detached
.sigfile per package made by an individual packager. The packagers' keys are themselves certified by a small set of master keys shipped in thearchlinux-keyringpackage;pacmantrusts a packager key because of those certifications. TheSigLevelsetting inpacman.confdecides whether package and database signatures are required. - Gentoo signs the repository.
The ebuild repository carries
Manifestfiles with checksums of every ebuild and of every source archive it downloads, and the repository as a whole is signed:emerge-webrsyncverifies a signed snapshot, andemerge --syncover rsync verifies the signed top-levelManifest. Binary packages from Gentoo's binary package host are signed individually.
Whatever the level, the trust anchor is a set of public keys on your system.
Adding a third-party repository means adding a key, and the question that matters is what that key is then trusted for.
A key that's trusted for every repository lets a third party sign replacements for any package on the system, including the C library.
Debian and Ubuntu let you bind a key to one repository with Signed-By; RPM and pacman keep a single system-wide keyring.
How to add a third-party repository safely shows how to keep that exposure small on each family.
Dependency resolution¶
When you ask to install one package, the package manager must find a set of package versions that satisfies every relationship at once: the package's dependencies, their dependencies, version constraints such as "libssl3 at least 3.0.2", conflicts, and the packages already installed that must keep working. Several packages may provide the same capability, so there are choices to make, and a choice made early can make a later constraint impossible to satisfy. In general this is a hard combinatorial problem (it's equivalent to boolean satisfiability), and the resolvers differ mainly in how they approach it:
- dnf delegates to libsolv, a dedicated SAT solver, which explores the whole space and can explain why no solution exists.
Its
best=Truedefault (set in/etc/dnf/dnf.confon the RHEL family) makes it fail rather than quietly choose an older version when the newest one can't be installed. - apt has historically used a heuristic resolver that makes good choices quickly in the common case and explains failures less clearly, preferring to hold packages back rather than remove installed ones during a plain upgrade.
apt 3.1 made a new solver the default: Ubuntu 26.04's apt 3.2 uses it, while Debian 13's apt 3.0 offers it only when you add
--solver 3.0. - pacman resolves dependencies by name and version against its synchronised databases, with little search: Arch's repositories are kept mutually consistent, so there's rarely more than one candidate to choose.
- Portage builds a dependency graph that includes USE-conditional dependencies and slots (several versions of one package installed side by side), and backtracks when it hits a conflict. Because changing a USE flag can add or remove dependencies, its graph is larger and its resolution slower than the others'.
Resolution is also what makes removal interesting.
Packages pulled in only as dependencies are recorded as automatically installed; when nothing depends on them any more, apt autoremove, dnf autoremove and emerge --depclean can identify and remove them, and on Arch pacman -Qdtq lists them for removal with pacman -Rns.
Marking a package as manually installed, or adding it to Portage's @world set, protects it from that clean-up.
Transactions¶
Once the resolver has a plan, the package manager applies it as a transaction: download everything, verify every signature and checksum, check for file conflicts, and only then start changing the system. Doing the checks first means that most failures (a missing package on a mirror, a bad signature, two packages claiming the same file) stop the operation before anything has been touched.
Applying the changes is a different matter.
None of these tools can make the installation of hundreds of files and the running of maintainer scripts truly atomic.
If the power fails half-way through, some packages are new, some old and one may be half-unpacked.
Each tool records enough to recover: dpkg marks packages as unpacked but not configured, so dpkg --configure -a can finish the job; rpm and pacman keep a lock and a database that's updated as each package completes; Portage merges one package at a time, so emerge --resume can continue a run that stopped.
Transactions are also the unit of history.
dnf numbers every transaction and records exactly which package versions it changed, so dnf history undo can compute the reverse operation.
apt writes each run to /var/log/apt/history.log, pacman to /var/log/pacman.log and Portage to /var/log/emerge.log.
Undoing a change requires more than a log, though: it needs the old package versions to still be available somewhere, and maintainer scripts that ran during the upgrade (a database schema migration, for example) aren't undone by reinstalling an older package.
That's why rolling back an update is reliable on some families and best-effort on others.
Configuration files¶
Upgrades must not destroy local changes to configuration files, and each family has a convention for what happens when both you and the package changed one:
| Family | Behaviour when both sides changed a configuration file |
|---|---|
| Debian/Ubuntu | dpkg stops and asks whether to keep your version or install the package's; the other copy is kept as .dpkg-old or .dpkg-dist |
| RHEL/Alma/Rocky | Your file stays in place and the new one is written as .rpmnew (or, depending on how the package marks the file, yours is moved aside as .rpmsave) |
| Arch | Your file stays in place and the new one is written as .pacnew |
| Gentoo | Files under CONFIG_PROTECT paths are never overwritten; new versions are written as ._cfg0000_<name> for you to merge with dispatch-conf or etc-update |
Why the families differ¶
The four families' tools grew up separately and reflect their distributions' priorities.
Debian built dpkg in the mid-1990s and apt on top of it at the end of that decade, for a volunteer project with tens of thousands of packages and multi-year stable releases.
Its strengths are careful policy (the Debian Policy Manual governs how every package behaves), interactive handling of configuration and a strong separation between the release and its updates.
Ubuntu inherits all of it.
Red Hat built RPM in the same period, with signatures on every package from early on, as befits software sold to enterprises that redistribute and audit it.
yum added repositories and dependency resolution; dnf replaced yum with libsolv and a transaction history designed to be queried and reversed.
RHEL 10 and its rebuilds ship dnf 4; Fedora has moved on to dnf 5, which isn't covered here.
Arch wrote pacman for a rolling-release distribution that values simplicity: one tool, one plain-text configuration file, packages that are close to what upstream ships and a build system (makepkg and PKGBUILDs) short enough to read.
Its design assumes the whole system is upgraded together; installing a newer package without upgrading the rest ("partial upgrade") isn't supported.
Gentoo took its model from the BSD ports collections: the repository holds build recipes, and Portage compiles each package on your machine with the features you've chosen. That makes its package manager a build system as much as an installer, with consequences explored in Binary and source distributions.
The tools' everyday commands differ, but the underlying operations map onto each other closely; the package manager command reference sets them side by side.
Further reading¶
- Debian Policy Manual, in particular the chapters on binary packages and maintainer scripts
- SecureApt on the Debian Wiki: how apt verifies repositories
- RPM documentation and libsolv
- DNF documentation (dnf 4)
- pacman home page and
pacman(8) - Gentoo Handbook: Portage
Related¶
- Explanation: Binary and source distributions
- Reference: Package manager commands
- Reference: Repository configuration
- How-to: How to find which package provides a file
- How-to: How to add a third-party repository safely
- How-to: How to hold a package at its current version
- How-to: How to roll back a package update