Skip to main content

Linux Package Management: apt, dnf, Snap and Flatpak

Install, update and remove software on Ubuntu, Debian, Fedora and RHEL with apt and dnf, add third-party repositories safely, and use Snap and Flatpak.

On Windows and macOS you often download an installer from a vendor's website. On Linux, you ask the package manager. It downloads software from your distribution's repositories, checks the signatures, installs everything the program depends on, and later updates all of it with one command. Security fixes for your web server, your SSH daemon and your kernel all arrive the same way.

This part covers apt on Ubuntu and Debian and dnf on Fedora and RHEL in depth, then the other package managers, Snap and Flatpak, and the mistakes that break systems.

Prerequisites

  • A Linux machine where you can use sudo (Part 2).
  • The command line basics from Part 3.

The apt output below comes from a fresh Ubuntu 24.04.5 LTS image and the dnf output from a Fedora 44 image (dnf5 5.4.3). Both ran on an arm64 machine, so Ubuntu downloads from ports.ubuntu.com and package names end in arm64 or aarch64. On a typical x86 PC or cloud server, you see archive.ubuntu.com, security.ubuntu.com and amd64 instead. Versions and sizes change with every update.

How Package Management Works

  • A package is an archive with the program's files, metadata (name, version, description) and a list of dependencies: other packages it needs.
  • A repository is a server that publishes packages plus a signed index of everything it offers. Your system trusts a repository through its signing key.
  • The high-level tool (apt, dnf) reads the index, works out the dependencies, downloads the packages and checks their signatures. The low-level tool (dpkg, rpm) installs a single package file and records what it installed.
Family Distributions Package format High-level tool Low-level tool
Debian Debian, Ubuntu, Linux Mint, Pop!_OS .deb apt (apt-get in scripts) dpkg
Red Hat Fedora, RHEL, Rocky Linux, AlmaLinux, CentOS Stream, Amazon Linux .rpm dnf (yum is an alias) rpm
SUSE openSUSE, SUSE Linux Enterprise .rpm zypper rpm
Arch Arch Linux, Manjaro .pkg.tar.zst pacman pacman
Alpine Alpine Linux (common in containers) .apk apk apk

Use the package manager of your distribution, and only repositories built for your exact release.

apt on Ubuntu and Debian

Where Packages Come From

Current Ubuntu releases describe their repositories in the deb822 format, one file per source, in /etc/apt/sources.list.d/. The old one-line /etc/apt/sources.list only holds a pointer now:

Each stanza lists the URL, the suites (noble is the codename of 24.04; -updates and -security carry fixes; -backports newer versions), the components (main is supported by Canonical, universe by the community, restricted and multiverse hold non-free software) and the key that signs it. On Debian 13 ("trixie") the components are main, contrib, non-free and non-free-firmware. Debian 13 still writes the one-line format during installation:

deb http://deb.debian.org/debian trixie main non-free-firmware
deb http://security.debian.org/debian-security trixie-security main non-free-firmware
deb http://deb.debian.org/debian trixie-updates main non-free-firmware

The one-line format still works. apt 3.0, which ships with Debian 13, has sudo apt modernize-sources to convert old files to deb822.

Update the Index and See What Can Be Upgraded

apt update downloads the current index from every repository. It installs nothing. Run it before you install or upgrade anything:

To install the upgrades:

sudo apt upgrade         # upgrade installed packages, never remove any
sudo apt full-upgrade    # also remove or replace packages when an upgrade needs it

apt upgrade lists what it will do and asks before it starts. Ubuntu rolls out some updates gradually (phased updates), so you may see "The following upgrades have been deferred due to phasing"; they arrive within a few days. If a kernel or core library was updated, the file /var/run/reboot-required appears and the login message says a reboot is needed.

Search and Inspect Packages

apt policy (also apt-cache policy) is the command to run when you wonder why apt picks a certain version: it lists every version it knows, the repository each comes from, and the priority that decides between them.

Install Packages

jq needed two libraries, so apt installed them too and marked them as automatically installed. Without -y, apt shows the plan and asks Do you want to continue? [Y/n]. Read that list, especially lines that say "REMOVED".

Ask dpkg what a package installed, and which package owns a file:

ii in the first column means the package is installed and configured. rc means it was removed but its configuration files are still on disk.

Remove Packages and Clean Up

  • apt remove deletes the program but keeps its configuration in /etc. apt purge deletes the configuration too.
  • apt autoremove removes dependencies nothing needs any more, here the two libraries jq pulled in.
  • apt-mark hold stops a package from being upgraded, for example a database server you upgrade by hand. Remember to unhold it, or it never gets security fixes.
  • sudo apt clean deletes downloaded package files from /var/cache/apt/archives when you need the disk space.

To install a .deb file you downloaded, give apt the path (with ./), so it also installs the dependencies: sudo apt install ./package.deb. Plain dpkg -i does not resolve dependencies.

Tip

apt is meant for people at a terminal; its output format may change between versions. In scripts and Dockerfiles, use apt-get, which has a stable interface, and run apt-get update in the same step as apt-get install -y --no-install-recommends ... so you never install from an old index.

Add a Third-Party Repository the Modern Way

Some software comes from the vendor's own repository: Docker, HashiCorp, Kubernetes, PostgreSQL, Node.js and many more. Older tutorials add the vendor's key with apt-key add. Do not follow them: apt-key trusted the key for every repository on the system, it was deprecated for years, and Debian 13 and Ubuntu 25.04 and later no longer ship it.

The current method stores the key in its own file and references it with Signed-By, so the key can only sign packages from that one repository. These are Docker's official instructions for Ubuntu, which use a deb822 .sources file:

The new repository shows up in apt update, and apt policy shows the candidate version from it. From here, sudo apt install docker-ce would install Docker from Docker's repository, and apt upgrade keeps it updated. The Docker guide continues from this point.

A few rules for third-party repositories:

  • Every repository you add can install code that runs as root on your machine. Add only vendors you trust, and only over HTTPS.
  • Check that the vendor supports your release. A repository for an older codename may install packages built against libraries you do not have.
  • Store keys in /etc/apt/keyrings/ (your own) and reference them with Signed-By. Packages that ship their own keys use /usr/share/keyrings/.
  • PPAs (Personal Package Archives on Launchpad) are third-party repositories too. sudo add-apt-repository ppa:owner/name (from the software-properties-common package) adds the source and its key in one step.

Python, pip and "externally-managed-environment"

Since Debian 12 and Ubuntu 23.04, pip install outside a virtual environment stops with an error. That protects the Python packages your system tools depend on from being overwritten:

Install Python libraries that a system tool needs with apt (sudo apt install python3-requests). For your own projects, create a virtual environment with python3 -m venv .venv (install python3-venv first on Ubuntu) and run pip inside it. For command-line tools written in Python, use pipx install <tool>. Avoid --break-system-packages unless you understand what it can break.

dnf on Fedora, RHEL, Rocky and AlmaLinux

Fedora 41 and later use DNF5, a rewrite in C++. RHEL 10, Rocky Linux 10 and AlmaLinux 10 still use DNF 4. Everyday commands are the same in both; the differences are in output and in a few subcommands such as config-manager. On RHEL and its rebuilds, yum is a link to dnf.

Repositories are .repo files in /etc/yum.repos.d/:

Install, query and remove a package:

Add a third-party repository by downloading its .repo file. The config-manager syntax changed in DNF5:

On RHEL 10 and its rebuilds (DNF 4), the same step is sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo, which needs the dnf-plugins-core package. dnf imports the repository's GPG key the first time you install from it and asks you to confirm the fingerprint; compare it with the one the vendor publishes.

apt and dnf Side by Side

Task Ubuntu / Debian Fedora / RHEL
Refresh the index sudo apt update automatic (sudo dnf makecache to force it)
Upgrade everything sudo apt upgrade sudo dnf upgrade
List available upgrades apt list --upgradable dnf check-upgrade
Install sudo apt install pkg sudo dnf install pkg
Remove sudo apt remove pkg sudo dnf remove pkg
Remove unused dependencies sudo apt autoremove sudo dnf autoremove
Search apt search word dnf search word
Details apt show pkg dnf info pkg
Which package owns a file dpkg -S /path rpm -qf /path
Which package provides a missing file apt-file search name dnf provides '*/name'
Files in a package dpkg -L pkg rpm -ql pkg
Hold a version sudo apt-mark hold pkg sudo dnf versionlock add pkg
History and undo /var/log/apt/history.log dnf history list, sudo dnf history undo <id>
Install a group of packages sudo apt install build-essential sudo dnf group install development-tools
Automatic security updates unattended-upgrades (on by default on Ubuntu) dnf5-plugin-automatic (Fedora), dnf-automatic (RHEL 10)

On RHEL 10, dnf versionlock and dnf provides work as shown, and versionlock needs the python3-dnf-plugin-versionlock package. Group names differ between distributions, so run dnf group list first.

Other Package Managers

Task openSUSE (zypper) Arch (pacman) Alpine (apk)
Refresh and upgrade sudo zypper refresh && sudo zypper update sudo pacman -Syu apk update && apk upgrade
Install sudo zypper install pkg sudo pacman -S pkg apk add pkg
Remove sudo zypper remove pkg sudo pacman -Rs pkg apk del pkg
Search zypper search word pacman -Ss word apk search word

On Arch, always upgrade the whole system with pacman -Syu before installing; pacman -Sy pkg on its own (a partial upgrade) can leave libraries out of step. In Alpine-based container images, use apk add --no-cache pkg so the index does not stay in the image.

Snap, Flatpak and AppImage

These formats bundle an application with its dependencies, so one build runs on many distributions. They are most useful for desktop applications and for tools that need a newer version than your distribution ships.

  • Snap is Canonical's format with a central store. Snaps run confined and update automatically, which is convenient on desktops and surprising on servers; control the timing with sudo snap refresh --hold or snap set system refresh.timer=....
  • Flatpak targets desktop applications, mostly from Flathub, and runs them in a sandbox.
  • AppImage is a single executable file. It does not update itself and is not checked by any package manager, so download it only from the project's own site.

On servers, prefer your distribution's packages or the vendor's official repository. They get security updates through the same apt upgrade or dnf upgrade as everything else.

Building From Source

When software is not packaged, the classic build is:

sudo apt install build-essential        # compilers and make (Fedora: sudo dnf group install development-tools)
./configure --prefix=/usr/local
make
sudo make install

Installing into /usr/local keeps your files apart from the distribution's. The package manager does not know about them, so it will not update or remove them, and nothing tells you about security fixes. For anything you run in production, a package, a vendor repository or a container image is easier to keep patched.

Troubleshooting

"Could not get lock /var/lib/dpkg/lock-frontend". Another apt process is running, often unattended-upgrades right after boot. Wait for it to finish (ps aux | grep -E 'apt|dpkg'). Do not delete the lock files.

"NO_PUBKEY" or "The following signatures couldn't be verified". The repository's key is missing or expired. Download the current key from the vendor's documentation into /etc/apt/keyrings/ and check that the Signed-By path in the .sources file matches.

"Unmet dependencies" or a half-finished install. Run sudo apt --fix-broken install, then sudo dpkg --configure -a if dpkg was interrupted.

"The following packages have been kept back". On Ubuntu this is usually a phased update; wait. Otherwise the upgrade needs to add or remove packages, which sudo apt full-upgrade allows after you review the list.

404 errors for an old release. Releases that reached end of life move to old-releases.ubuntu.com (Ubuntu) or archive.debian.org (Debian). Upgrade to a supported release instead of pointing at the archive.

dnf: "Error: GPG check FAILED". The package was not signed with a key dnf trusts. Import the vendor's key (sudo rpm --import <url>) after you have checked its fingerprint. Do not set gpgcheck=0.

Common Mistakes

  • Following apt-key tutorials. Use a keyring file and Signed-By as shown above.
  • Adding repositories for another distribution or release, for example a Debian repository on Ubuntu or a jammy repository on noble. Dependencies break in ways that are hard to undo.
  • Piping a download into sudo bash without reading the script first.
  • Skipping apt update before installing, and getting 404 errors for package versions the mirror has already replaced.
  • Holding packages and forgetting them. Check apt-mark showhold or dnf versionlock list during upgrades.
  • Using pip install --break-system-packages instead of a virtual environment.

Summary

  • Packages come from signed repositories; apt and dnf resolve dependencies, dpkg and rpm install single files.
  • On Ubuntu and Debian: apt update, apt upgrade, apt install, apt remove, apt autoremove, and dpkg -L / dpkg -S to query.
  • Repositories live in deb822 .sources files in /etc/apt/sources.list.d/, with keys in their own keyring files referenced by Signed-By. apt-key is gone.
  • On Fedora and RHEL: dnf install, dnf upgrade, dnf remove, rpm -qf / rpm -ql, and .repo files in /etc/yum.repos.d/.
  • Snap, Flatpak and AppImage bundle dependencies and suit desktop apps; servers are easier to patch with distribution packages.
  • Use virtual environments for Python libraries instead of the system pip.

Next: User and Group Management, where you create accounts, manage groups and set up sudo and SSH keys.

Keep Learning

Updated:

Found an issue?