Package Managers Deep Dive
Why This Matters
It is 2 AM and your monitoring system fires an alert: a critical security vulnerability has been disclosed in OpenSSL. Every server you manage needs to be patched before business hours. Without a package manager, you would need to manually download source code on each server, compile it, figure out which files to replace, and hope you do not break anything. With a package manager, the fix is one command per server -- or one Ansible playbook for all of them.
Package managers are the backbone of software management on Linux. They handle downloading software, resolving dependencies, tracking installed files, applying updates, and cleanly removing programs. Every Linux administrator uses a package manager dozens of times a day. Understanding how they work -- not just the basic commands, but the underlying architecture -- separates administrators who react to problems from those who prevent them.
This chapter takes you deep into the three major package management ecosystems: APT (Debian/Ubuntu), DNF/YUM (Fedora/RHEL), and Pacman (Arch Linux). By the end, you will be able to install, query, pin, clean, and troubleshoot packages on any major distribution.
Try This Right Now
Check which package manager your system uses and how many packages are currently installed:
# Debian/Ubuntu
$ dpkg --list | wc -l
# Fedora/RHEL
$ rpm -qa | wc -l
# Arch Linux
$ pacman -Q | wc -l
You will likely see hundreds or even thousands of packages. Every one of those was installed, tracked, and has its dependencies satisfied by your package manager.
What Package Managers Actually Do
A package manager is not just an installer. It is a database administrator, a dependency solver, a file tracker, and a download manager rolled into one.
┌──────────────────────────────────────────────────────────┐
│ PACKAGE MANAGER │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ Repository │ │ Dependency │ │ Local │ │
│ │ Metadata │ │ Resolver │ │ Database │ │
│ │ (what's │ │ (what else │ │ (what's │ │
│ │ available) │ │ is needed) │ │ installed) │ │
│ └──────┬──────┘ └──────┬───────┘ └────────┬────────┘ │
│ │ │ │ │
│ v v v │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Package Operations │ │
│ │ Install | Update | Remove | Query | Verify │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ File System │ │
│ │ /usr/bin /usr/lib /etc /usr/share ... │ │
│ └─────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
The Core Functions
- Installation: Download a package and all its dependencies, verify integrity, extract files to the correct locations
- Dependency resolution: If package A needs libraries from packages B and C, install B and C first
- Tracking: Record every file installed by every package so nothing gets lost
- Updates: Compare installed versions against repository versions, upgrade what is outdated
- Removal: Remove a package and optionally its no-longer-needed dependencies
- Querying: Search for packages, list files owned by a package, find which package provides a file
Package Formats
The two dominant binary package formats on Linux are:
| Format | Extension | Used By | Tool |
|---|---|---|---|
| Debian | .deb | Debian, Ubuntu, Mint, Pop!_OS | dpkg |
| RPM | .rpm | Fedora, RHEL, CentOS, openSUSE | rpm |
Arch Linux uses its own compressed tar archive format (.pkg.tar.zst).
Think About It: Why do Linux distributions use binary packages at all? Why not just distribute source code and compile everything on the user's machine? (Hint: think about time, resources, and reproducibility.)
The APT Ecosystem (Debian/Ubuntu)
APT is a layered system. Understanding the layers prevents confusion about which tool to use when.
┌───────────────────────────────────────────┐
│ apt (high-level, user-friendly CLI) │ <-- Use this
├───────────────────────────────────────────┤
│ apt-get / apt-cache (classic CLI tools) │ <-- Use in scripts
├───────────────────────────────────────────┤
│ libapt (APT library, C++) │
├───────────────────────────────────────────┤
│ dpkg (low-level package installer) │ <-- Installs .deb files
└───────────────────────────────────────────┘
- dpkg: Installs, removes, and queries individual
.debfiles. Does not resolve dependencies or download packages. - apt-get / apt-cache: The classic high-level tools. They handle repositories, downloads, and dependency resolution. Stable interface, preferred in scripts.
- apt: The modern unified command. Combines the most common functions of
apt-getandapt-cachewith a nicer interface (progress bars, color). Preferred for interactive use.
sources.list: Where Packages Come From
APT fetches packages from repositories defined in /etc/apt/sources.list and files under /etc/apt/sources.list.d/.
$ cat /etc/apt/sources.list
A typical entry looks like:
deb http://deb.debian.org/debian bookworm main contrib non-free-firmware
deb-src http://deb.debian.org/debian bookworm main contrib non-free-firmware
deb http://security.debian.org/debian-security bookworm-security main contrib non-free-firmware
Breaking down a line:
deb http://deb.debian.org/debian bookworm main contrib non-free-firmware
^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
type repository URL release components
(binary
or source)
- deb: Binary packages. deb-src: Source packages.
- URL: The mirror hosting the packages.
- Release: The codename (bookworm, jammy) or class (stable, testing).
- Components: Sections within the repository.
mainis fully free software.contribandnon-freecontain packages with different licensing.
Distro Note: Ubuntu uses components named
main,restricted,universe, andmultiverse. Debian usesmain,contrib, andnon-free. The naming differs but the concept is the same.
Modern DEB822 Format
Newer Debian and Ubuntu releases are migrating to the DEB822 .sources format in /etc/apt/sources.list.d/:
Types: deb deb-src
URIs: http://deb.debian.org/debian
Suites: bookworm bookworm-updates
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
This format is more readable and explicitly ties repositories to their signing keys.
Essential APT Commands
Update the package index (always do this first):
$ sudo apt update
Hit:1 http://deb.debian.org/debian bookworm InRelease
Get:2 http://security.debian.org/debian-security bookworm-security InRelease [48.0 kB]
Fetched 48.0 kB in 1s (42.3 kB/s)
Reading package lists... Done
Building dependency tree... Done
15 packages can be upgraded. Run 'apt list --upgradable' to see them.
Install a package:
$ sudo apt install nginx
Reading package lists... Done
Building dependency tree... Done
The following additional packages will be installed:
libnginx-mod-http-geoip2 libnginx-mod-http-image-filter ...
The following NEW packages will be installed:
libnginx-mod-http-geoip2 libnginx-mod-http-image-filter nginx nginx-common nginx-core
0 upgraded, 5 newly installed, 0 to remove and 15 not upgraded.
Need to get 1,847 kB of archives.
Do you want to continue? [Y/n]
Upgrade all installed packages:
$ sudo apt upgrade # Safe upgrade: never removes packages
$ sudo apt full-upgrade # May remove packages to resolve conflicts
Safety Warning:
apt full-upgradecan remove packages. Always review what it proposes before confirming. On production servers, preferapt upgradeand handle conflicts manually.
Search for packages:
$ apt search "web server"
nginx/stable 1.22.1-9 amd64
small, powerful, scalable web/reverse proxy server
apache2/stable 2.4.57-2 amd64
Apache HTTP Server
Show package details:
$ apt show nginx
Package: nginx
Version: 1.22.1-9
Depends: nginx-core (<< 1.22.1-9.1~) | nginx-full (<< 1.22.1-9.1~) ...
Description: small, powerful, scalable web/reverse proxy server
Remove a package:
$ sudo apt remove nginx # Removes the package, keeps config files
$ sudo apt purge nginx # Removes the package AND config files
$ sudo apt autoremove # Removes orphaned dependencies
List installed packages:
$ apt list --installed
$ apt list --installed | grep nginx
dpkg: The Low-Level Tool
When you need to work with individual .deb files or query the package database directly:
# Install a local .deb file
$ sudo dpkg -i package.deb
# List all installed packages
$ dpkg -l
# List files installed by a package
$ dpkg -L nginx
/.
/usr
/usr/sbin
/usr/sbin/nginx
/usr/share/doc/nginx
...
# Find which package owns a file
$ dpkg -S /usr/sbin/nginx
nginx-core: /usr/sbin/nginx
# Show package status
$ dpkg -s nginx
When dpkg -i fails due to missing dependencies, fix it with:
$ sudo dpkg -i some-package.deb
dpkg: dependency problems prevent configuration of some-package:
some-package depends on libfoo; however: Package libfoo is not installed.
$ sudo apt install -f # Fix broken dependencies
PPAs (Ubuntu)
Personal Package Archives let developers distribute packages outside the official repositories. They are common on Ubuntu.
# Add a PPA
$ sudo add-apt-repository ppa:deadsnakes/ppa
$ sudo apt update
$ sudo apt install python3.12
Safety Warning: PPAs are not officially vetted. Only add PPAs from sources you trust. A malicious PPA could replace system packages with compromised versions.
To remove a PPA:
$ sudo add-apt-repository --remove ppa:deadsnakes/ppa
$ sudo apt update
APT Pinning
Sometimes you want to hold a package at a specific version or prefer packages from one repository over another. This is called pinning.
Hold a package at its current version:
$ sudo apt-mark hold nginx
nginx set on hold.
$ sudo apt upgrade
The following packages have been kept back:
nginx
Release the hold:
$ sudo apt-mark unhold nginx
Install a specific version:
# List available versions
$ apt list -a nginx
nginx/stable 1.22.1-9 amd64
nginx/oldstable 1.18.0-6.1+deb11u3 amd64
# Install a specific version
$ sudo apt install nginx=1.22.1-9
For advanced pinning, create a file in /etc/apt/preferences.d/:
Package: nginx
Pin: version 1.22.1-9
Pin-Priority: 1001
Priority values: 1001 forces a downgrade if needed, 500 is the default, 100 means only install if no other version is available.
The DNF/YUM Ecosystem (Fedora/RHEL)
DNF (Dandified YUM) is the successor to YUM. On RHEL 8+ and Fedora 22+, dnf is the standard. On older systems (RHEL 7 and earlier), yum is used. The command syntax is nearly identical.
┌──────────────────────────────────────────┐
│ dnf (high-level package manager) │
├──────────────────────────────────────────┤
│ libdnf / hawkey (dependency solver) │
├──────────────────────────────────────────┤
│ rpm (low-level package installer) │
└──────────────────────────────────────────┘
Repository Configuration
Repos are defined in /etc/yum.repos.d/ as .repo files:
$ cat /etc/yum.repos.d/fedora.repo
[fedora]
name=Fedora $releasever - $basearch
metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch
Key fields:
- enabled: 1 (active) or 0 (disabled)
- gpgcheck: 1 to verify package signatures (always leave this on)
- gpgkey: Path to the GPG key for signature verification
- baseurl or metalink: Where to find packages
Essential DNF Commands
# Update repository metadata
$ sudo dnf check-update
# Install a package
$ sudo dnf install nginx
Dependencies resolved.
===========================================================================
Package Arch Version Repository Size
===========================================================================
Installing:
nginx x86_64 1:1.24.0-1.fc39 updates 42 k
Installing dependencies:
nginx-core x86_64 1:1.24.0-1.fc39 updates 589 k
nginx-filesystem noarch 1:1.24.0-1.fc39 updates 11 k
Transaction Summary
===========================================================================
Install 3 Packages
Total download size: 642 k
Is this ok [y/N]:
Upgrade packages:
$ sudo dnf upgrade # Upgrade all packages
$ sudo dnf upgrade nginx # Upgrade a specific package
$ sudo dnf upgrade --security # Only security updates
Search and info:
$ dnf search "web server"
$ dnf info nginx
$ dnf provides /usr/sbin/nginx # Find which package provides a file
Remove packages:
$ sudo dnf remove nginx
$ sudo dnf autoremove # Remove unneeded dependencies
List packages:
$ dnf list installed
$ dnf list available
$ dnf list updates
Transaction history:
One of DNF's most powerful features -- every operation is recorded and can be undone:
$ dnf history
ID | Command line | Date and time | Action(s) | Altered
-------+---------------------------+------------------+------------+--------
15 | install nginx | 2024-03-15 10:22 | Install | 3
14 | upgrade | 2024-03-14 22:00 | Upgrade | 28
13 | remove httpd | 2024-03-10 14:15 | Removed | 5
# Undo a transaction
$ sudo dnf history undo 15 # Removes nginx and its dependencies
RPM: The Low-Level Tool
# Install a local .rpm file
$ sudo rpm -ivh package.rpm
# Query all installed packages
$ rpm -qa
# Query info about an installed package
$ rpm -qi nginx
# List files in an installed package
$ rpm -ql nginx
# Find which package owns a file
$ rpm -qf /usr/sbin/nginx
nginx-core-1.24.0-1.fc39.x86_64
# Verify package integrity (check if files have been modified)
$ rpm -V nginx
S.5....T. c /etc/nginx/nginx.conf
The verification output tells you exactly what changed. The flags mean:
- S: File size differs
- 5: MD5 checksum differs
- T: Modification time differs
- c: This is a config file
DNF Modules (RHEL/CentOS Stream)
RHEL uses modules to provide multiple versions of software from the same repository:
$ dnf module list nodejs
Name Stream Profiles Summary
nodejs 18 common [d], development Javascript runtime
nodejs 20 common [d], development Javascript runtime
$ sudo dnf module enable nodejs:20
$ sudo dnf install nodejs
Version Locking with DNF
# Install the versionlock plugin
$ sudo dnf install python3-dnf-plugin-versionlock
# Lock a package
$ sudo dnf versionlock add nginx
Adding versionlock on: nginx-1:1.24.0-1.fc39.*
# List locked packages
$ sudo dnf versionlock list
# Remove a lock
$ sudo dnf versionlock delete nginx
Think About It: DNF records a full transaction history that can be undone. Why is this valuable on production servers? What could go wrong if an upgrade breaks something?
The Pacman Ecosystem (Arch Linux)
Pacman is Arch Linux's package manager. It is fast, simple, and does not try to do more than it needs to.
Essential Pacman Commands
Pacman uses single-letter flags. The main operations are:
- -S: Sync (install/update from repositories)
- -R: Remove
- -Q: Query (local database)
- -F: File query (which package provides a file)
# Synchronize package databases and upgrade all packages
$ sudo pacman -Syu
:: Synchronizing package databases...
core 130.4 KiB 1234 KiB/s 00:00
extra 8.7 MiB 5.32 MiB/s 00:02
multilib 147.5 KiB 1567 KiB/s 00:00
:: Starting full system upgrade...
there is nothing to do
# Install a package
$ sudo pacman -S nginx
# Search for a package
$ pacman -Ss "web server"
extra/nginx 1.24.0-1
Lightweight HTTP server and IMAP/POP3 proxy server
# Show package info
$ pacman -Si nginx # Remote info
$ pacman -Qi nginx # Local (installed) info
# List files installed by a package
$ pacman -Ql nginx
# Find which package owns a file
$ pacman -Qo /usr/bin/nginx
# Remove a package and its unneeded dependencies
$ sudo pacman -Rsu nginx
# Remove a package, dependencies, and config files
$ sudo pacman -Rns nginx
Safety Warning: On Arch Linux, always run
pacman -Syu(full system upgrade) before installing new packages. Partial upgrades (installing a package without upgrading) can break your system because Arch is a rolling release.
The AUR (Arch User Repository)
The AUR is a community-driven repository of build scripts (PKGBUILDs) for software not in the official repos. It is one of the largest software repositories on Linux.
Important: AUR packages are not officially vetted. You should always inspect the PKGBUILD before building.
Using makepkg manually:
# Install base-devel group (build tools)
$ sudo pacman -S --needed base-devel git
# Clone the AUR package
$ git clone https://aur.archlinux.org/yay.git
$ cd yay
# Inspect the build script
$ cat PKGBUILD
# Build and install
$ makepkg -si
Using an AUR helper (yay):
Once you have yay installed, it wraps pacman for both official and AUR packages:
$ yay -S some-aur-package
$ yay -Syu # Upgrade everything, including AUR packages
Pacman Configuration
Pacman is configured in /etc/pacman.conf:
[options]
HoldPkg = pacman glibc
Architecture = auto
Color
CheckSpace
ParallelDownloads = 5
[core]
Include = /etc/pacman.d/mirrorlist
[extra]
Include = /etc/pacman.d/mirrorlist
Useful options:
- ParallelDownloads: Download multiple packages simultaneously (significant speed improvement)
- Color: Colorize output
- IgnorePkg: Skip specific packages during upgrades (equivalent to version pinning)
# Pin/ignore a package
IgnorePkg = linux linux-headers
Comparing the Three Ecosystems
| Task | APT (Debian/Ubuntu) | DNF (Fedora/RHEL) | Pacman (Arch) |
|---|---|---|---|
| Update repo metadata | apt update | dnf check-update | pacman -Sy |
| Install | apt install pkg | dnf install pkg | pacman -S pkg |
| Remove | apt remove pkg | dnf remove pkg | pacman -R pkg |
| Remove + deps | apt autoremove | dnf autoremove | pacman -Rsu pkg |
| Upgrade all | apt upgrade | dnf upgrade | pacman -Syu |
| Search | apt search term | dnf search term | pacman -Ss term |
| Show info | apt show pkg | dnf info pkg | pacman -Si pkg |
| List files | dpkg -L pkg | rpm -ql pkg | pacman -Ql pkg |
| Find owner of file | dpkg -S /path | rpm -qf /path | pacman -Qo /path |
| List installed | apt list --installed | dnf list installed | pacman -Q |
| Pin/hold version | apt-mark hold pkg | dnf versionlock add pkg | IgnorePkg in conf |
| Clean cache | apt clean | dnf clean all | pacman -Sc |
Cleaning Package Caches
Package managers cache downloaded packages. Over time, this can consume significant disk space.
# APT: See cache size, then clean
$ du -sh /var/cache/apt/archives/
847M /var/cache/apt/archives/
$ sudo apt clean # Remove ALL cached packages
$ sudo apt autoclean # Remove only obsolete cached packages
# DNF: Clean metadata and packages
$ sudo dnf clean all
$ sudo dnf clean packages # Only remove cached packages
# Pacman: Clean cache
$ sudo pacman -Sc # Remove old cached packages (keeps current)
$ sudo pacman -Scc # Remove ALL cached packages
Distro Note: On servers with automated updates, the package cache can grow to several gigabytes. Schedule periodic cache cleaning in a cron job or systemd timer.
Security Updates
Applying security patches quickly is critical for any system exposed to the internet.
APT: Security Updates Only
# List security updates
$ apt list --upgradable 2>/dev/null | grep -i security
# Install only security updates (Debian)
$ sudo apt upgrade -y -o Dir::Etc::SourceList=/etc/apt/sources.list \
-o Dir::Etc::SourceParts=/dev/null \
-t bookworm-security
# Ubuntu has unattended-upgrades for automatic security patches
$ sudo apt install unattended-upgrades
$ sudo dpkg-reconfigure -plow unattended-upgrades
DNF: Security Updates Only
$ sudo dnf upgrade --security
$ dnf updateinfo list security
Automatic Updates
For production servers, consider enabling automatic security updates:
# Debian/Ubuntu: unattended-upgrades
$ cat /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
# Fedora/RHEL: dnf-automatic
$ sudo dnf install dnf-automatic
$ sudo systemctl enable --now dnf-automatic-install.timer
Safety Warning: Even automatic security updates carry risk. A kernel update requires a reboot. A library update could break an application. On critical production servers, test updates in a staging environment first.
Hands-On: Package Investigation Lab
Let us practice querying the package database. These exercises work on any distribution -- just use the appropriate commands from the comparison table above.
Exercise 1: Trace a binary to its package
# Step 1: Find the full path of a command
$ which curl
/usr/bin/curl
# Step 2: Find which package owns it
# Debian/Ubuntu:
$ dpkg -S /usr/bin/curl
curl: /usr/bin/curl
# Fedora/RHEL:
$ rpm -qf /usr/bin/curl
curl-8.2.1-1.fc39.x86_64
# Arch:
$ pacman -Qo /usr/bin/curl
/usr/bin/curl is owned by curl 8.5.0-1
Exercise 2: Examine package dependencies
# Debian/Ubuntu: Show what a package depends on
$ apt depends curl
curl
Depends: libc6 (>= 2.34)
Depends: libcurl4 (= 7.88.1-10+deb12u5)
Depends: zlib1g (>= 1:1.1.4)
# And the reverse -- what depends on this package
$ apt rdepends curl
# Fedora/RHEL:
$ dnf repoquery --requires curl
$ dnf repoquery --whatrequires curl
# Arch:
$ pacman -Si curl | grep Depends
$ pacman -Qi curl | grep "Required By"
Exercise 3: Find packages that provide a specific file
# You need a file but don't know which package provides it
# Debian/Ubuntu:
$ apt-file search libssl.so
libssl-dev: /usr/lib/x86_64-linux-gnu/libssl.so
libssl3: /usr/lib/x86_64-linux-gnu/libssl.so.3
# (Install apt-file first: sudo apt install apt-file && sudo apt-file update)
# Fedora/RHEL:
$ dnf provides */libssl.so
# Arch:
$ pacman -F libssl.so
Debug This
A colleague reports that apt update is failing on a Debian server:
$ sudo apt update
Err:1 http://deb.debian.org/debian bookworm InRelease
Could not resolve 'deb.debian.org'
Err:2 http://security.debian.org/debian-security bookworm-security InRelease
Could not resolve 'security.debian.org'
Reading package lists... Done
W: Failed to fetch http://deb.debian.org/debian/dists/bookworm/InRelease
Could not resolve 'deb.debian.org'
E: Some index files failed to download. They have been ignored, or old ones used instead.
What would you check?
-
DNS resolution: Can the server resolve any hostname?
$ nslookup deb.debian.org $ cat /etc/resolv.conf -
Network connectivity: Can you reach the internet at all?
$ ping -c 3 8.8.8.8 -
Proxy settings: Is the server behind a proxy?
$ env | grep -i proxy
Now here is a different failure -- a colleague tries to install a package and gets:
$ sudo apt install libfoo-dev
E: Unable to locate package libfoo-dev
Checklist:
- Did you run
sudo apt updatefirst? - Is the package name correct? (
apt search libfoo) - Is the package in a component that is not enabled? (Check
universeon Ubuntu) - Is the package only available for a different architecture?
- Has the package been renamed or replaced?
What Just Happened?
┌──────────────────────────────────────────────────────────────┐
│ │
│ In this chapter, you learned: │
│ │
│ - Package managers handle installation, dependency │
│ resolution, updates, removal, and tracking. │
│ │
│ - APT (Debian/Ubuntu): apt/apt-get for high-level ops, │
│ dpkg for low-level .deb manipulation, sources.list │
│ for repository configuration. │
│ │
│ - DNF (Fedora/RHEL): dnf for high-level ops, rpm for │
│ low-level .rpm work, .repo files in /etc/yum.repos.d/. │
│ Transaction history with undo capability. │
│ │
│ - Pacman (Arch): Single tool with -S/-R/-Q/-F flags. │
│ AUR for community packages. Rolling release requires │
│ full-system upgrades. │
│ │
│ - Version pinning prevents unwanted upgrades. │
│ │
│ - Cache cleaning recovers disk space. │
│ │
│ - Security updates should be prioritized and can be │
│ automated with unattended-upgrades or dnf-automatic. │
│ │
│ - You can trace any file to its package, examine │
│ dependencies, and find which package provides a file. │
│ │
└──────────────────────────────────────────────────────────────┘
Try This
Exercises
-
Inventory exercise: List all installed packages on your system, sort them by installed size, and find the 10 largest. (Hint: on Debian/Ubuntu, try
dpkg-query -W --showformat='${Installed-Size}\t${Package}\n' | sort -rn | head -10) -
Dependency tree: Pick a complex package (like
firefoxornginx) and map out its dependency tree. How many packages does it pull in? (Hint:apt depends --recurseordnf repoquery --requires --resolve --recursive) -
File hunt: Without installing it, find out what files the
stracepackage would place on your system. (Hint: on Debian, useapt-file list strace; on Fedora, usednf repoquery -l strace) -
Repository management: On a test system, add a third-party repository (like the Docker official repo), install a package from it, then cleanly remove both the package and the repository.
-
Version pinning: On a test system, install a package, pin it to the current version, run a full upgrade, and confirm the pinned package was held back.
Bonus Challenge
Write a shell script that works on both Debian/Ubuntu and Fedora/RHEL systems. The script should detect which distribution it is running on (using /etc/os-release), then use the appropriate package manager to: update the package index, list available security updates, and report how many packages need updating. This is the kind of multi-distro script you will write in real operations work.
What's Next
You now know how to install software from repositories, but what do you do when the package you need is not available in any repository, or you need a newer version with custom options? Chapter 58 teaches you how to compile software from source code -- the original way of installing software on Unix and Linux.