Package Management
Installing, updating and removing software the Linux way: packages, repositories, apt and dnf, and universal formats such as Snap and Flatpak.
What a package is
A software package is one file that contains everything needed to install a program: the program's files (binaries, libraries, default configuration, documentation, manual pages), metadata (name, version, architecture, a description, and which other packages it needs), and small scripts that run before or after installation or removal — for example to create a system user or to restart a service after an update.
There are two main formats, one per distribution family (see distributions and families):
-
.debon Debian, Ubuntu and their relatives. Inside is a control archive (metadata and the maintainer scriptspreinst,postinst,prerm,postrm) and a data archive with the files. -
.rpm(originally "Red Hat Package Manager") on Red Hat Enterprise Linux (RHEL), Fedora, Rocky Linux, AlmaLinux and SUSE: a header with the metadata and scriptlets, followed by the files.
A package file name tells you most of the metadata at a glance:
htop_3.3.0-4build1_amd64.deb name_version-revision_architecture.deb htop-3.2.2-2.el9.x86_64.rpm name-version-release.architecture.rpm
amd64 and x86_64 are two names for the
same 64-bit PC architecture; arm64 and
aarch64 are the 64-bit ARM equivalents, and
all / noarch marks a package that runs
anywhere (scripts, data). The part after the upstream version
(-4build1, -2.el9) is the distribution's
own revision: the same upstream version can be rebuilt or patched
several times.
Once a package is installed, the package manager records every file it placed. That database is what makes clean removal, verification and "which package owns this file?" possible — and it is the reason to avoid copying programs into system directories by hand.
Repositories and trust
A package repository is a server (or directory) that holds packages together with an index: a list of every package, its version, its dependencies and a checksum. Your system keeps a local copy of the indexes of the repositories it is configured to use, and the package manager searches that copy.
-
Debian and Ubuntu list repositories in
/etc/apt/sources.listand/etc/apt/sources.list.d/. Ubuntu 24.04 uses the newer multi-line format in/etc/apt/sources.list.d/ubuntu.sources. -
RHEL and Fedora use one
.repofile per repository in/etc/yum.repos.d/. On RHEL, the official repositories (BaseOS and AppStream) are enabled through the Red Hat subscription.
Signing keys
Packages usually travel over the Internet, sometimes through mirror servers run by universities or companies you have never heard of. What protects you is a digital signature. The distribution signs its repository metadata (apt) or each package (rpm) with a private GNU Privacy Guard (GPG) key; your system has the matching public key and checks every download against it. A package that was changed on the way, or that comes from someone without the private key, fails the check and is refused.
-
apt checks the signature on the repository's
InRelease(orRelease) file. That file lists checksums of the package indexes, and the indexes list checksums of every package — so one signature protects the whole chain. -
dnf checks the signature of each package (
gpgcheck=1in the.repofile) and can also check the repository metadata (repo_gpgcheck=1).
The trust you place in a key is total: whoever holds the private key can ship any software to your machine, and the install scripts run as root. That is why adding a repository deserves the care described in third-party repositories below.
Dependencies
A dependency is another package that a package needs in order to work — usually a shared library, sometimes a tool or an interpreter. Instead of every program bringing its own copy of, say, the OpenSSL library, one copy is installed and shared. When a security fix for that library arrives, one update fixes every program that uses it.
The package metadata lists the dependencies with version limits
(Depends: libc6 (>= 2.38), libncursesw6 (>= 6)).
Installing a package therefore often installs a few others; removing
a library that other packages need would remove those too, and the
package manager warns you before it does. Packages installed only as
a dependency are marked automatically installed, so they
can be cleaned up once nothing needs them any more.
Low-level and high-level tools
Each family has two layers of tools. The low-level tool installs one package file that is already on disk and maintains the database; it does not download anything and does not resolve dependencies. The high-level tool talks to the repositories, works out the full set of packages needed, downloads them, and then calls the low-level tool.
| Family | Package format | Low-level tool | High-level tool |
|---|---|---|---|
| Debian, Ubuntu | .deb |
dpkg |
apt (interactive), apt-get /
apt-cache (scripts)
|
| RHEL, Fedora, Rocky, Alma | .rpm |
rpm |
dnf (yum on old systems) |
| SUSE, openSUSE | .rpm |
rpm |
zypper |
apt (Advanced Package Tool) is the
high-level package manager of Debian and Ubuntu. The
apt command is meant for people at a keyboard: it has
progress bars and colors, and its output may change between
versions. In scripts, use apt-get and
apt-cache, whose output is kept stable — which is why
apt prints "WARNING: apt does not have a stable CLI
interface" when its output goes into a pipe.
dnf (Dandified YUM) is the high-level
package manager of Fedora, RHEL and their rebuilds. It replaced
yum (Yellowdog Updater, Modified); on RHEL 8 and later
the yum command still exists as a link to
dnf, so old instructions keep working. Fedora 41 and
later ship DNF5, a rewrite with the same everyday commands.
The apt workflow
Every change to installed software needs root, so these commands run
with sudo; looking things up does not.
update, upgrade and full-upgrade
apt update downloads the latest indexes from every
configured repository. It changes no software at all; it only tells
apt what is available.
$ sudo apt update
Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease Get:2 http://archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB] Get:3 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB] Get:4 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 Packages [1,178 kB] Fetched 1,430 kB in 2s (715 kB/s) Reading package lists... Done Building dependency tree... Done Reading state information... Done 12 packages can be upgraded. Run 'apt list --upgradable' to see them.
Hit means the index had not changed since last time;
Get means a newer one was downloaded.
noble is the code name of Ubuntu 24.04, and
noble-security is the repository with security fixes.
Then the actual update:
$ apt list --upgradable
$ sudo apt upgrade
Listing... Done libssl3t64/noble-updates,noble-security 3.0.13-0ubuntu3.6 amd64 [upgradable from: 3.0.13-0ubuntu3.5] openssl/noble-updates,noble-security 3.0.13-0ubuntu3.6 amd64 [upgradable from: 3.0.13-0ubuntu3.5] … Reading package lists... Done Building dependency tree... Done Reading state information... Done Calculating upgrade... Done The following packages will be upgraded: libssl3t64 openssl … 12 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Need to get 9,874 kB of archives. After this operation, 45.1 kB of additional disk space will be used. Do you want to continue? [Y/n]
Always read that summary before answering Y, in particular the "to remove" count. The three levels differ in what they are allowed to do:
-
apt upgradeinstalls newer versions of installed packages. It may install new packages when an upgrade needs them, but it never removes an installed package; an upgrade that would need a removal is held back ("kept back"). -
apt full-upgrade(apt-get dist-upgradein the older tool) may also remove packages when that is the only way to complete the upgrade. It is the normal way to apply updates that change dependencies, but read the list of removals. -
apt-get upgradeis the most careful: it neither installs new packages nor removes any.
None of these move you to a new release of the distribution (for
example Ubuntu 24.04 to 26.04); that is a separate, planned
procedure (do-release-upgrade on Ubuntu). Patch policy
and timing are covered in
Updates, Patches and Drivers.
install, remove, purge and autoremove
$ sudo apt install htop
Reading package lists... Done Building dependency tree... Done Reading state information... Done The following additional packages will be installed: libnl-genl-3-200 Suggested packages: lm-sensors lsof strace The following NEW packages will be installed: htop libnl-genl-3-200 0 upgraded, 2 newly installed, 0 to remove and 12 not upgraded. Need to get 184 kB of archives. After this operation, 482 kB of additional disk space will be used. Do you want to continue? [Y/n]
The dependency (libnl-genl-3-200) is added
automatically. Suggested packages are optional extras that
are not installed unless you ask for them. -y answers
yes in advance, which is useful in scripts but skips your chance to
read the summary.
| Command | Removes program files |
Removes configuration in /etc
|
Removes unused dependencies |
|---|---|---|---|
sudo apt remove nginx |
Yes | No | No |
sudo apt purge nginx |
Yes | Yes | No |
sudo apt autoremove |
— | No (add --purge) |
Yes, all that nothing needs any more |
remove keeps configuration files on purpose: if you
reinstall, your settings come back. purge is for when
you want the package really gone, or want to start over with the
default configuration. Neither touches data such as databases or
websites under /var or users' home directories, as a
rule.
search, show and list
$ apt search --names-only '^nginx'
$ apt show nginx
$ apt list --installed
Sorting... Done Full Text Search... Done nginx/noble-updates,noble-security 1.24.0-2ubuntu7.4 amd64 small, powerful, scalable web/proxy server nginx-common/noble-updates,noble-security 1.24.0-2ubuntu7.4 all small, powerful, scalable web/proxy server - common files … Package: nginx Version: 1.24.0-2ubuntu7.4 Priority: optional Section: web Origin: Ubuntu Depends: nginx-common (= 1.24.0-2ubuntu7.4), libc6 (>= 2.34), libcrypt1 (>= 1:4.1.0), libpcre2-8-0 (>= 10.22), libssl3t64 (>= 3.0.0), zlib1g (>= 1:1.2.0) … Listing... Done adduser/noble,now 3.137ubuntu1 all [installed,automatic] bash/noble,now 5.2.21-2ubuntu4 amd64 [installed] …
search looks in names and descriptions (so it finds a
lot; --names-only narrows it). show prints
the metadata, including the dependencies. In the list,
[installed,automatic] marks packages that came in as a
dependency — the ones autoremove may clean up later.
What dpkg knows
dpkg -l prints the package database directly. The first
two letters of each line are the desired state and the
actual state: ii is installed,
rc is removed with configuration files still present (a
package that was removed, not purged), and
hi is installed and held. Because the listing is plain
text, the filters from
Pipes, Redirection and Filters
work on it. Here a saved listing is summarized:
cat > packages.txt <<'EOF'
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-=======================-============-=================================
rc apache2 2.4.58-1ubuntu8.4 amd64 Apache HTTP Server
ii bash 5.2.21-2ubuntu4 amd64 GNU Bourne Again SHell
ii git 1:2.43.0-1ubuntu7.3 amd64 fast, scalable, distributed revision control system
ii htop 3.3.0-4build1 amd64 interactive processes viewer
ii nginx 1.24.0-2ubuntu7.4 amd64 small, powerful, scalable web/proxy server
hi postgresql-16 16.10-0ubuntu0.24.04.1 amd64 object-relational SQL database, version 16 server
ii tmux 3.4-1ubuntu0.1 amd64 terminal multiplexer
rc vim-tiny 2:9.1.0016-1ubuntu7.8 amd64 Vi IMproved - enhanced vi editor - compact version
EOF
echo "Packages per state:"
awk 'NR > 5 {print $1}' packages.txt | sort | uniq -c
echo "Held at their current version:"
awk '$1 == "hi" {print " " $2, $3}' packages.txt
echo "Removed, configuration files left behind:"
awk '$1 == "rc" {print " " $2}' packages.txt
Packages per state:
1 hi
5 ii
2 rc
Held at their current version:
postgresql-16 16.10-0ubuntu0.24.04.1
Removed, configuration files left behind:
apache2
vim-tiny
NR > 5 skips the five header lines;
$1 is the state column and $2 the name. On
a real system you would pipe dpkg -l straight into the
same awk commands, and the rc list is
exactly what a sudo apt purge could clean up. On RHEL,
rpm -qa lists the installed packages, one per line as
name-version-release.arch (for example
nginx-core-1.20.1-22.el9_6.3.x86_64).
apt and dnf side by side
Most everyday tasks map one to one. The main difference in habit:
dnf refreshes its metadata by itself when the cached
copy is old, so there is no separate "update the lists" step, and
dnf upgrade (or its alias dnf update) does
everything in one command.
| Task | Debian / Ubuntu | RHEL / Fedora |
|---|---|---|
| Refresh the package lists | sudo apt update |
automatic; dnf check-update shows what is new
|
| Install all updates | sudo apt upgrade |
sudo dnf upgrade |
| Updates that may remove packages | sudo apt full-upgrade |
sudo dnf upgrade (handles obsoleted packages)
|
| Install a package | sudo apt install nginx |
sudo dnf install nginx |
| Install a downloaded file | sudo apt install ./app.deb |
sudo dnf install ./app.rpm |
| Remove a package | sudo apt remove nginx |
sudo dnf remove nginx |
| Remove with configuration | sudo apt purge nginx |
no equivalent; changed configuration files are kept as
.rpmsave
|
| Remove unneeded dependencies | sudo apt autoremove |
sudo dnf autoremove |
| Search | apt search nginx |
dnf search nginx |
| Show details | apt show nginx |
dnf info nginx |
| List installed packages | apt list --installed |
dnf list --installed |
| Which package owns this file? | dpkg -S /usr/bin/htop |
rpm -qf /usr/bin/htop |
| Which package would provide a file? |
apt-file search bin/dig (install
apt-file first)
|
dnf provides '*/bin/dig' |
| List a package's files | dpkg -L htop |
rpm -ql htop |
| History of changes | /var/log/apt/history.log |
dnf history |
A typical install on RHEL 9 (trimmed):
$ sudo dnf install nginx
Updating Subscription Management repositories. Last metadata expiration check: 0:42:10 ago on Wed 30 Sep 2026 09:12:44 AM UTC. Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: nginx x86_64 2:1.20.1-22.el9_6.3 rhel-9-for-x86_64-appstream-rpms 36 k Installing dependencies: nginx-core x86_64 2:1.20.1-22.el9_6.3 rhel-9-for-x86_64-appstream-rpms 571 k nginx-filesystem noarch 2:1.20.1-22.el9_6.3 rhel-9-for-x86_64-appstream-rpms 9.6 k redhat-logos-httpd noarch 90.4-2.el9 rhel-9-for-x86_64-appstream-rpms 18 k Transaction Summary ================================================================================ Install 4 Packages Total download size: 635 k Installed size: 1.8 M Is this ok [y/N]:
Note the default answer: apt's prompt is [Y/n] (Enter
means yes), dnf's is [y/N] (Enter means no).
Holding a version
Sometimes one package must stay at its current version — a database server whose upgrade needs a planned migration, or a kernel module that only builds against the current kernel. Holding (apt) or version locking (dnf) excludes it from upgrades until you release it:
$ sudo apt-mark hold postgresql-16
$ apt-mark showhold
$ sudo apt-mark unhold postgresql-16
postgresql-16 set on hold. postgresql-16 Canceled hold on postgresql-16.
On RHEL the versionlock plugin does the same (sudo dnf versionlock add postgresql-server; on RHEL 9 install
python3-dnf-plugin-versionlock first). For more
control, apt pinning (priorities in
/etc/apt/preferences.d/) decides which repository wins
when several offer the same package.
Adding a third-party repository safely
Vendors such as database, monitoring or container companies often publish their own repository so that you get their newest version and their updates through the normal package manager. That is much better than a one-off download: you do get updates. But remember the trust rule — the vendor's key can now install anything as root. Add only repositories from vendors you would trust with root, from their official documentation.
The safe pattern on Debian and Ubuntu stores the vendor's key in its
own file and ties it to that one repository with
signed-by, so the key cannot vouch for packages from
any other repository (the fictional vendor here uses the reserved
domain example.com):
$ sudo install -d -m 0755 /etc/apt/keyrings
$ sudo curl -fsSL https://packages.example.com/apt/key.asc -o /etc/apt/keyrings/example.asc
$ gpg --show-keys /etc/apt/keyrings/example.asc
$ echo "deb [signed-by=/etc/apt/keyrings/example.asc] https://packages.example.com/apt stable main" | sudo tee /etc/apt/sources.list.d/example.list
$ sudo apt update
$ sudo apt install example-agent
-
Create
/etc/apt/keyrings, the conventional place for repository keys you add yourself. -
Download the vendor's public key over HTTPS (Hypertext Transfer
Protocol Secure).
-ffails on server errors,-sShides progress but shows errors,-Lfollows redirects. - Show the key's fingerprint and compare it with the one the vendor publishes. This is the step that actually establishes trust.
-
Add the repository line.
signed-by=names the key file;stableis the suite andmainthe component, as the vendor documents them. - Refresh the lists and install.
signed-by limits which key may sign the repository, not
what the repository may replace: a vendor repository can still offer
a newer version of any package. Pin it
(/etc/apt/preferences.d/) if it should only provide its
own packages.
On RHEL the vendor supplies a .repo file for
/etc/yum.repos.d/; check that it contains
gpgcheck=1 and a gpgkey= line pointing to
the vendor's key. Extra Packages for Enterprise Linux (EPEL), run by
the Fedora project, is the best-known extra repository for RHEL.
Snap, Flatpak and AppImage
Distribution packages share libraries, which keeps systems small and patches simple — but it also means an application can only be as new as the libraries of your release allows. Three formats take the opposite approach: the application brings its dependencies with it and works across distributions.
Snap
A Snap is a self-contained application
package from Canonical, the company behind Ubuntu. The
snapd service installs snaps from the Snap Store,
updates them automatically in the background. Most snaps run
confined (sandboxed) with only the access their interfaces
allow; "classic" snaps, which the install command must allow
explicitly (--classic), have full system access. Snaps
work for command-line tools, desktop applications and server
software; on Ubuntu some programs (Firefox, for example) are
delivered only as a snap.
$ snap find nextcloud
$ sudo snap install nextcloud
$ snap list
$ sudo snap refresh
Each snap is a compressed image that is mounted when the system
starts, which is why an Ubuntu machine shows several
/dev/loop devices in df and
lsblk. That is normal, not a problem.
Flatpak
Flatpak is a cross-distribution format for desktop applications, most often installed from the Flathub repository. Applications are built on shared runtimes (for example a GNOME or KDE runtime) instead of the host's libraries, and run in a sandbox that asks for access to files or devices through portals. Fedora Workstation and many other desktops include it by default.
$ flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
$ flatpak install flathub org.gimp.GIMP
$ flatpak update
AppImage
An AppImage is a single executable file that
contains the application and its libraries. You download it, make it
executable (chmod +x) and run it; nothing is installed
and there is no daemon. The flip side: no automatic updates, no
sandbox by default, and no repository signature checking what you
downloaded — you are back to trusting a download page.
| Distribution package | Snap | Flatpak | AppImage | |
|---|---|---|---|---|
| Libraries | Shared with the system | Bundled | Bundled or shared runtimes | Bundled |
| Updates | With the system (apt / dnf) | Automatic | flatpak update or software center |
Manual |
| Sandbox | No | Mostly (confinement; not for "classic" snaps) | Yes | No, by default |
| Best for | Servers, system tools, anything security-critical | Newer apps on Ubuntu, some server apps | Desktop apps on any distribution | Trying a desktop app without installing |
| Downsides | Versions can be old on long-term support releases | Larger, slower first start; one store run by Canonical | Larger downloads; desktop only | No updates or integration |
A reasonable rule on servers: use the distribution's packages first, the vendor's signed repository second, and a snap or container image third.
Installing from source, and where files go
When no package exists at all, you can build a program from its
source code, classically with ./configure,
make and sudo make install. Treat that as
the last resort: the package manager does not know about those
files, so they get no security updates, cannot be removed cleanly,
and may collide with a package installed later. If you must, keep
the default prefix /usr/local (which the package
manager never touches) and write down what you built and why.
Where a package puts its files
Because the database records every file, you can always ask where a
package installed its parts, and the answer follows the
file system layout: programs in /usr/bin, configuration in
/etc, documentation in /usr/share/doc, and
a systemd unit in /usr/lib/systemd/system for anything
that runs as a service.
$ dpkg -L htop
$ dpkg -S /usr/bin/htop
/. /usr /usr/bin /usr/bin/htop /usr/share /usr/share/applications /usr/share/applications/htop.desktop /usr/share/doc /usr/share/doc/htop /usr/share/doc/htop/AUTHORS /usr/share/doc/htop/README.gz /usr/share/doc/htop/changelog.Debian.gz /usr/share/man/man1/htop.1.gz … htop: /usr/bin/htop
rpm -ql htop and
rpm -qf /usr/bin/htop give the same answers on RHEL,
and rpm -qc lists only the configuration files.
rpm -V nginx (verify) compares the installed files with
the database and reports what has been changed since installation —
useful when you suspect someone edited or replaced a system file.