Chapter Eleven

Package Management

Learning Objectives
  1. Explain why packages exist and what problems they solve
  2. Use apt, dnf, and pacman confidently for everyday tasks
  3. Compare traditional package managers with newer sandboxed formats
  4. Install software from source when no package is available
  5. Recognise and avoid the symptoms of dependency hell

In the early days of Unix, installing a new program meant downloading a tarball, extracting it, running ./configure && make && make install, and crossing your fingers. It worked, but it was tedious, it scattered files across the system with no record of what came from where, and uninstalling was often just "try to remember what got put where". Package management was invented to fix this. Today, every serious Linux distribution ships with a package manager, and it is the tool you will use most often as an administrator after the shell itself.

Why Packages?

A package is a single archive file that bundles together everything an installable piece of software needs: its binaries, libraries, configuration files, documentation, default settings, and — crucially — metadata. The metadata describes the package's name and version, lists its dependencies (other packages it needs to work), specifies which files it installs and where, and includes pre- and post-install scripts that can configure the system or start services.

With this metadata, a package manager can do things that manual installation cannot:

  • Track every file installed on the system and what package owns it.
  • Resolve dependencies automatically, installing prerequisites when you ask for a package.
  • Upgrade packages cleanly, preserving your configuration files.
  • Roll back or remove software completely, without orphaned files.
  • Verify the integrity of installed files with checksums.
  • Apply security updates across the whole system in one command.

Think of a package manager as a database of everything installed, plus a network of remote repositories holding the available packages, plus the machinery to keep the two in sync. The effect is transformative: on a Linux machine, applying a year's worth of security fixes to every installed program can be a single command.

The Two Main Families

Most Linux distributions trace their package format to one of two traditions.

Debian and its descendants (Ubuntu, Linux Mint, Raspberry Pi OS, Pop!_OS) use the .deb format, with dpkg as the low-level tool and apt as the high-level front end.

Red Hat and its descendants (Fedora, RHEL, CentOS Stream, Rocky, AlmaLinux) use the .rpm format, with rpm as the low-level tool and dnf (previously yum) as the high-level front end.

openSUSE also uses the .rpm format and is therefore often grouped with the Red Hat family for convenience, but its lineage is independent: SUSE was founded in Germany in 1994, originally based on Slackware, and has always evolved alongside Red Hat rather than from it. Its high-level front end is zypper.

These three traditions cover more than 90% of Linux installations. A few distributions — Arch, Alpine, Gentoo, Void — use their own formats, which are worth knowing but rarer in production.

apt: The Debian Way

On Debian and Ubuntu, apt is the command you will use daily. It replaced the older apt-get and apt-cache commands around 2014 with a friendlier, more colourful interface.

sudo apt update                 # refresh the package list from repositories
sudo apt upgrade                # upgrade all installed packages
sudo apt install nginx          # install a package
sudo apt install nginx=1.22.0-1 # install a specific version
sudo apt remove nginx           # remove, leaving config files
sudo apt purge nginx            # remove, including config files
sudo apt autoremove             # remove unused dependencies
apt search webserver            # search available packages
apt show nginx                  # detailed info about a package
apt list --installed            # list installed packages

The distinction between update and upgrade trips up newcomers. update refreshes the local cache of what is available; upgrade actually installs the newer versions. You almost always run update before upgrade.

The underlying tool is dpkg, which operates on individual .deb files:

sudo dpkg -i package.deb        # install a .deb file
sudo dpkg -r package            # remove
dpkg -l                         # list installed
dpkg -L nginx                   # list files owned by a package
dpkg -S /etc/nginx/nginx.conf   # which package owns this file?

dpkg does not handle dependencies by itself; if you dpkg -i a package with missing dependencies, it will complain. Running sudo apt install -f (for "fix broken") resolves the missing pieces.

Repositories are configured in /etc/apt/sources.list and /etc/apt/sources.list.d/*.list. Each line points to a mirror carrying packages for a particular distribution and component (main, contrib, non-free, universe, multiverse). Adding a third-party repository — for Docker, Node.js, or Kubernetes — is a matter of dropping a .list file and an accompanying GPG key.

dnf: The Red Hat Way

On Fedora and its downstream cousins, dnf is the command.

sudo dnf check-update           # refresh and show available updates
sudo dnf upgrade                # install all available updates
sudo dnf install nginx          # install a package
sudo dnf remove nginx           # remove
sudo dnf autoremove             # remove unused dependencies
dnf search webserver            # search
dnf info nginx                  # detailed info
dnf list installed              # list installed
dnf history                     # see every transaction ever performed

The last one is a nice touch: dnf history gives you a chronological log of every install, upgrade, or removal, with the ability to roll back a specific transaction.

Underneath, dnf uses rpm for low-level operations:

sudo rpm -i package.rpm         # install
sudo rpm -e nginx               # erase (remove)
rpm -qa                         # query all installed
rpm -qf /etc/nginx/nginx.conf   # which package owns this file?
rpm -ql nginx                   # list files in this package

Repositories live in /etc/yum.repos.d/*.repo. Adding a new repository, like RPM Fusion for multimedia codecs, means placing a new .repo file there.

Arch and pacman

Arch Linux and its derivatives (Manjaro, EndeavourOS) use pacman, one of the fastest and most elegant package managers around.

sudo pacman -Syu                # sync, refresh, upgrade (do this often)
sudo pacman -S nginx            # install
sudo pacman -R nginx            # remove
sudo pacman -Rs nginx           # remove and unused dependencies
pacman -Ss nginx                # search
pacman -Q                       # list installed
pacman -Qi nginx                # info about an installed package

The -Syu invocation, for Sync/refresh-Y/Upgrade, is the daily Arch ritual. Arch is a rolling release distribution, meaning there are no version numbers or release cycles — you simply upgrade whenever you feel like it, always receiving the latest upstream versions.

The AUR (Arch User Repository) deserves a mention. It is not a traditional binary repository but a collection of community-maintained build scripts (called PKGBUILDs) for software not in the official repositories. Tools like yay or paru automate fetching, building, and installing from the AUR. It is one of the largest informal software collections in Linux, but because the scripts are community-maintained, you should read before running.

openSUSE and zypper

openSUSE uses zypper, an apt-style tool operating on RPM packages:

sudo zypper refresh
sudo zypper update
sudo zypper install nginx
sudo zypper remove nginx
zypper search nginx

If you can drive apt, zypper feels familiar within an hour.

The New Wave: Snap, Flatpak, AppImage

Traditional package managers install software system-wide and share libraries across packages, which is efficient but inflexible: everything must use the same version of a library at the same time. A new wave of formats trades efficiency for independence.

Snap, developed by Canonical, packages applications with all their dependencies into a single squashfs image, confined by AppArmor profiles. Snaps are automatically updated and work across distributions. They are the default for several Ubuntu desktop applications.

snap find vlc
sudo snap install vlc
snap list
sudo snap remove vlc

Flatpak is an alternative designed with desktop applications in mind, widely used on Fedora and promoted by GNOME. Flatpaks run in sandboxes based on bubblewrap, with fine-grained portals for accessing the filesystem, camera, printer, and so on. The central repository is Flathub.

flatpak install flathub org.videolan.VLC
flatpak run org.videolan.VLC
flatpak list
flatpak update

AppImage is the simplest of the three: a single executable file that contains the application and all its dependencies. You download it, make it executable, and run it. No installation, no root, no package manager involved.

chmod +x MyApp.AppImage
./MyApp.AppImage

Each of these formats has its champions and its critics. The pragmatic view is that traditional packages are best for system software and command-line tools, while sandboxed formats shine for desktop applications that need to run the same on many distributions.

Building from Source

Occasionally a program you need is not packaged anywhere, or you need a newer version than the repositories carry. The classic Unix dance is:

tar xzf myapp-1.2.3.tar.gz
cd myapp-1.2.3
./configure --prefix=/usr/local
make
sudo make install

The ./configure script probes your system for libraries and compilers and generates a Makefile. make compiles. make install copies the results into place. Modern projects increasingly use CMake or Meson in place of autoconf, so the incantation varies slightly, but the idea is the same.

When installing software from source, prefer /usr/local as the prefix rather than /usr — this keeps source-installed software separate from package-managed software, so they never fight over the same files. Better still, use checkinstall to build a quick .deb or .rpm that the package manager can track.

Dependency Hell

A package manager exists largely to solve dependency hell — the nightmare where package A needs library X version 2, package B needs library X version 3, and library X can only exist once on the system. A good package manager will refuse to install a conflicting combination and tell you why. The bad cases usually occur when you mix repositories that target different distribution versions, or when you install from source over a package-managed library.

The best defences against dependency hell are:

  1. Stick to your distribution's official repositories when possible.
  2. Use sandboxed formats (Snap, Flatpak) for applications that need newer libraries than your base system.
  3. Use containers (Chapter 17) for development environments that need exotic or conflicting dependencies.
  4. If you must install third-party software, prefer packages from the upstream vendor (e.g. Docker's own Debian repository) over random instructions from the internet.

A Package Manager Cheat Sheet

Memorise the ones for your distribution. Look up the others when you encounter them. Package management is where most routine Linux administration happens, and fluency here pays back quickly.