Package managers: apt, dnf, apk
How to install and update software across distros: repositories, update vs upgrade, and the difference between apt, dnf, and apk.
Installing software by downloading a binary and unpacking it by hand is a bad idea: nobody tracks dependencies, updates or removal. A package manager does this for you. It knows which libraries a program needs, installs them, updates them and can remove them cleanly.
Concepts
Package: an archive with a program, its files and a description. The description says which other packages it depends on and what to do after install.
Repository: a server with packages that the system trusts. The package
manager downloads packages from there and checks their signatures.
Repository addresses live in settings: on Ubuntu 24.04 that's
/etc/apt/sources.list.d/ubuntu.sources, on Alpine /etc/apk/repositories.
Index (package list): a local copy of the repository's catalog, that is, which packages and which versions are there. The package manager searches the index, not the server.
Which package manager is on a machine depends on the distro. This is exactly the kind of detail where distros differ. You can find out the distro like this:
cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.5 LTS"
NAME="Ubuntu"
...
ID=ubuntu
ID_LIKE=debian
...
| Family | Package manager | Package format |
|---|---|---|
| Debian, Ubuntu | apt, apt-get | .deb |
| RHEL, Rocky, AlmaLinux, Fedora | dnf (formerly yum) | .rpm |
| Alpine | apk | .apk |
A package manager from another family simply won't be found. On Alpine,
apt-get gives sh: apt-get: not found.
apt: Debian and Ubuntu
apt update # refresh the index from the repositories
apt install nginx # install a package
apt upgrade # upgrade installed packages
apt remove nginx # remove a package
apt search nginx # search for a package by name and description
People often mix up update and upgrade. apt update only refreshes
the local index: which packages and which versions are in the repositories. apt upgrade installs the actual updates. Run
apt update before installing, otherwise apt looks for the package in a
stale index.
The index can even be completely empty. That's the case in the Docker image
ubuntu:24.04: there, /var/lib/apt/lists/ is empty, and an install
without update fails:
apt-get install -y tree
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package tree
The package exists. After apt-get update, the same command works.
A few more commands you need every day:
apt-cache policy tree # which version is installed and which install will pick
dpkg -l tree # is the package installed (ii at the start of the line means "installed")
apt list --upgradable # what can be upgraded
apt autoremove # remove dependencies nobody needs anymore
apt-cache policy shows a Candidate: line: the version that install
will put in:
tree:
Installed: (none)
Candidate: 2.1.1-2ubuntu3.24.04.2
Version table:
2.1.1-2ubuntu3.24.04.2 500
500 http://ports.ubuntu.com/ubuntu-ports noble-updates/universe arm64 Packages
...
remove only removes the package itself. Libraries that apt installed
along with it stay behind, and apt says so directly. Here's what
apt-get remove -y jq prints:
The following packages were automatically installed and are no longer required:
libjq1 libonig5
Use 'apt autoremove' to remove them.
apt remove also leaves the package's configs in /etc. To remove those
too, there's apt purge.
dnf: RHEL, Rocky, AlmaLinux, Fedora
dnf install nginx
dnf upgrade
dnf remove nginx
dnf search nginx
A separate "refresh the index" step usually isn't needed here: dnf
downloads fresh repository metadata itself if the saved copy is stale. By
default, metadata is considered stale after 48 hours (metadata_expire in
/etc/dnf/dnf.conf). You can force a refresh with dnf makecache. The old
name for the command is yum. On RHEL 8 and newer, yum runs on top of
dnf and accepts the same commands.
apk: Alpine
apk add nginx # install
apk del nginx # remove, along with dependencies nobody needs
apk update # refresh the index
apk upgrade # upgrade packages
apk search nginx # search for a package
apk info -e nginx # is the package installed (prints the name if it is)
Alpine is minimalist, apk is fast. That's why Alpine is a common base for
small Docker images. If there's no index, apk add downloads it itself and
saves it to /var/cache/apk (about 2 MB). apk add --no-cache installs
the package and doesn't save the index.
In Dockerfile
Inside images there's a catch: you clean up the index after install, so the
image doesn't bloat. Otherwise the downloaded package lists stay in the
layer forever. In ubuntu:24.04, after apt-get update they take up about
56 MB.
# Debian/Ubuntu
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
# Alpine
RUN apk add --no-cache curl
There are three rules here, and each one closes off its own problem.
updateandinstallon the sameRUNline. EachRUNline is a separate layer. Ifupdateis on its own, Docker caches that layer and the next build installs packages from the old index.- Cleanup on the same
RUNline. A file removed in the next layer still sits in the previous one and takes up space. - The
-yflag. A build can't answer the questionDo you want to continue?. Without-y, apt printsAbort.and the build fails.
Scripts and Dockerfiles use apt-get, not apt. apt's output is meant
for people and can change between versions, which it warns about itself
when run outside a terminal:
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Packages and systemd
Many service packages bring their own unit file. On Debian and Ubuntu,
installing such a package immediately enables and starts the service:
after apt-get install nginx, a link to nginx.service appears in
/etc/systemd/system/multi-user.target.wants/. On Fedora and RHEL, by
default it's the opposite: the unit file is installed, but you need to
enable the service yourself with systemctl enable --now.
Common mistakes
E: Unable to locate packageright after a fresh install or in a container. The index is empty or stale, runapt-get updatefirst.apt-get: not foundorapk: not found. A command from another distro, check/etc/os-release.apt upgradeinstead ofapt update"to refresh the list".upgradewill install updates for every package, and on a server that's a separate decision.- A separate
RUN apt-get updatein a Dockerfile. The image is heavier, and a rebuild from cache installs old versions. aptin scripts and builds instead ofapt-get. It works, but with a warning and no guarantee about the output format.
What you'll need for the tasks
- Log into a container with
docker exec -it <name> shand exit withexit. - Find out the distro:
cat /etc/os-release. - Install packages:
apt-get update,apt-get install -y <package>,apk add <package>. Check the install:dpkg -l <package>,apk info -e <package>. - Build an image:
docker build -t <name>:<tag> <folder>, run it:docker run --rm <name>:<tag>, check its size:docker images <name>. - Edit a text file in any editor on your own machine.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson linux-admin-03-package-mgmt
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress