- linux
- l1
- topic-pack
- package-management --- Portal | Level: L1: Foundations | Topics: Package Management | Domain: Linux
Package Management - Primer¶
Why This Matters¶
Package management is day-1 Linux operations. Every server you touch runs packages you did not compile, and every production incident eventually traces back to a version mismatch, a broken dependency, or a repository you forgot to pin. Getting this wrong means silent drift across your fleet, security patches that never land, or a 3 AM apt upgrade that removes half your stack.
Core Concepts¶
1. The Two Families: Debian vs Red Hat¶
Linux package management splits along two lineages. You will encounter both in any fleet of meaningful size.
| Aspect | Debian/Ubuntu | RHEL/CentOS/Fedora |
|---|---|---|
| Low-level tool | dpkg |
rpm |
| High-level tool | apt / apt-get |
yum / dnf |
| Package format | .deb |
.rpm |
| Config directory | /etc/apt/ |
/etc/yum.repos.d/ |
| Package database | /var/lib/dpkg/ |
/var/lib/rpm/ |
| Cache directory | /var/cache/apt/archives/ |
/var/cache/yum/ or /var/cache/dnf/ |
Name origin:
aptstands for Advanced Package Tool.dpkgstands for Debian Package.rpmstands for RPM Package Manager (a recursive acronym, originally Red Hat Package Manager).dnfstands for Dandified YUM, andyumstands for Yellowdog Updater, Modified — originally written for Yellow Dog Linux, a PowerPC distribution.
dnf replaced yum starting with Fedora 22 and RHEL 8. On modern RHEL/CentOS Stream, yum is a symlink to dnf. Use dnf in new automation; support yum for legacy hosts.
2. Low-Level Tools: dpkg and rpm¶
These operate on individual package files. They do not resolve dependencies. Use them for inspection, forensics, and emergencies — not routine installs.
# Install a local .deb (no dependency resolution)
dpkg -i /tmp/custom-agent_2.4.1_amd64.deb
# If dependencies are broken after dpkg -i:
apt-get install -f
# List all installed packages (Debian)
dpkg -l | grep nginx
# Which package owns a file? (Debian)
dpkg -S /usr/sbin/nginx
# nginx-core: /usr/sbin/nginx
# Install a local .rpm
rpm -ivh /tmp/custom-agent-2.4.1.x86_64.rpm
# Query an installed package (Red Hat)
rpm -qi nginx
# Which package owns a file? (Red Hat)
rpm -qf /usr/sbin/nginx
# List all files in a package (Red Hat)
rpm -ql nginx
Debug clue: When
dpkg -ileaves a package in a broken state, you will see it marked asiF(installed, failed config) oriU(installed, unpacked but not configured) indpkg -loutput. The second and third characters in the status code tell you:i=installed,c=config-files only,U=unpacked,F=half-configured,H=half-installed.
Gotcha: dpkg -i will fail silently on missing dependencies and leave the package in a half-configured state. Always follow with apt-get install -f or better yet, use apt install ./package.deb which handles dependencies.
3. High-Level Tools: apt and dnf¶
These are what you use 99% of the time. They resolve dependencies, fetch from repositories, and handle upgrades.
# --- Debian/Ubuntu ---
# Update package index (does NOT upgrade anything)
apt update
# Upgrade all packages (safe — never removes packages)
apt upgrade -y
# Full upgrade (may remove packages to resolve conflicts)
apt full-upgrade -y
# Install a specific package
apt install -y nginx=1.24.0-1ubuntu1
# Remove package (keep config files)
apt remove nginx
# Remove package AND config files
apt purge nginx
# --- RHEL/CentOS/Fedora ---
# Refresh metadata
dnf check-update
# Install a package
dnf install -y nginx-1.24.0-1.el9
# Upgrade all packages
dnf upgrade -y
# Remove a package
dnf remove nginx
# Downgrade a package
dnf downgrade nginx-1.22.0-1.el9
Production rule: Never run apt upgrade or dnf upgrade interactively on a production server. Use automation (Ansible, unattended-upgrades) with a tested package set.
4. Searching and Listing¶
# Search for packages by name or description (Debian)
apt search "^nginx"
apt-cache search nginx
# Show package details before installing (Debian)
apt show nginx
apt-cache policy nginx
# Output:
# nginx:
# Installed: 1.24.0-1ubuntu1
# Candidate: 1.24.0-2ubuntu1
# Version table:
# 1.24.0-2ubuntu1 500
# 500 http://archive.ubuntu.com/ubuntu noble/main amd64 Packages
# *** 1.24.0-1ubuntu1 100
# 100 /var/lib/dpkg/status
# List installed packages matching a pattern (Debian)
apt list --installed 2>/dev/null | grep nginx
# Search for packages (Red Hat)
dnf search nginx
# Show package info (Red Hat)
dnf info nginx
# List installed packages (Red Hat)
dnf list installed | grep nginx
# List available versions of a package (Red Hat)
dnf list available nginx --showduplicates
5. Repositories¶
Repositories are where packages come from. Misconfigured repos are one of the top causes of fleet drift.
# --- Debian/Ubuntu ---
# Main repo config
cat /etc/apt/sources.list
# Additional repos (preferred location on modern Ubuntu)
ls /etc/apt/sources.list.d/
# Add a third-party repo
cat <<'EOF' > /etc/apt/sources.list.d/custom-app.list
deb [signed-by=/usr/share/keyrings/custom-app.gpg] https://packages.example.com/apt stable main
EOF
apt update
# --- RHEL/CentOS/Fedora ---
# List enabled repos
dnf repolist
# List all repos (including disabled)
dnf repolist all
# Add a repo
dnf config-manager --add-repo https://packages.example.com/rpm/stable/
# Enable/disable a repo
dnf config-manager --set-enabled epel
dnf config-manager --set-disabled epel-testing
Timeline:
apt-keywas deprecated in Debian 11 (2021) and Ubuntu 22.04 (2022). The newsigned-byapproach ties each repository to a specific GPG key, so a compromised third-party key cannot sign packages from other repositories. This is a significant security improvement over the old global keyring model.
Gotcha: On Ubuntu 22.04+, apt-key is deprecated. Use signed-by in the sources list pointing to a keyring file in /usr/share/keyrings/. Old tutorials still show apt-key add — this will break on modern systems.
6. GPG Keys and Repository Trust¶
Package managers verify signatures to ensure packages have not been tampered with. Skipping this is a security incident waiting to happen.
# --- Debian/Ubuntu (modern approach) ---
# Download and install a GPG key to the keyring directory
curl -fsSL https://packages.example.com/gpg.key | \
gpg --dearmor -o /usr/share/keyrings/example.gpg
# Reference in sources list
echo "deb [signed-by=/usr/share/keyrings/example.gpg] https://packages.example.com/apt stable main" \
> /etc/apt/sources.list.d/example.list
# --- RHEL/CentOS/Fedora ---
# Import a GPG key
rpm --import https://packages.example.com/RPM-GPG-KEY-example
# Verify a package signature
rpm -K /tmp/example-1.0.0.x86_64.rpm
# example-1.0.0.x86_64.rpm: digests signatures OK
# List imported GPG keys
rpm -qa gpg-pubkey*
Production consequence: If you disable GPG checks (--nogpgcheck or apt --allow-unauthenticated), a compromised mirror can push arbitrary binaries to your fleet. Treat every --nogpgcheck in your automation as a security finding.
7. Version Pinning and Holding Packages¶
In production, you pin versions to prevent unintended upgrades that break your application.
# --- Debian/Ubuntu: hold a package ---
apt-mark hold nginx
# nginx set on hold.
# Check held packages
apt-mark showhold
# nginx
# Release the hold
apt-mark unhold nginx
# --- Debian/Ubuntu: pin via preferences ---
cat <<'EOF' > /etc/apt/preferences.d/pin-nginx
Package: nginx
Pin: version 1.24.0-1ubuntu1
Pin-Priority: 1001
EOF
# --- RHEL/CentOS/Fedora: exclude from updates ---
# In /etc/dnf/dnf.conf or /etc/yum.conf:
# excludepkgs=nginx*
# Or per-command:
dnf upgrade -y --exclude=nginx*
# Using the versionlock plugin (dnf)
dnf install -y python3-dnf-plugin-versionlock
dnf versionlock add nginx-1.24.0-1.el9
dnf versionlock list
dnf versionlock delete nginx
| Priority | Effect (Debian) |
|---|---|
| 1001+ | Forces install even if downgrade |
| 990 | Default for target release |
| 500 | Default for non-target |
| 100 | Installed package priority |
| -1 | Never install |
Gotcha: A held package blocks apt upgrade from completing if other packages depend on a newer version. Your unattended-upgrades will log errors and silently skip security patches for the entire transaction. Monitor /var/log/unattended-upgrades/.
8. Security Updates¶
Applying security patches is non-negotiable. The question is how to do it without breaking production.
# --- Debian/Ubuntu ---
# List available security updates
apt list --upgradable 2>/dev/null | grep -i security
# Install only security updates (requires unattended-upgrades)
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
# Check unattended-upgrades config
cat /etc/apt/apt.conf.d/50unattended-upgrades | grep -A5 Allowed-Origins
# Manual security-only upgrade
apt upgrade -y -o Dir::Etc::SourceList=/etc/apt/sources.list \
-o Dir::Etc::SourceParts=/dev/null \
-t $(lsb_release -cs)-security
# --- RHEL/CentOS/Fedora ---
# List security updates
dnf updateinfo list security
# Install security updates only
dnf upgrade --security -y
# Check advisory details
dnf updateinfo info RHSA-2024:1234
Production pattern: Stage security updates through dev -> staging -> prod with a 24-48h bake time between stages. Automate the promotion. Never skip staging for "just a security patch."
9. Cache Management¶
Package caches grow unbounded and will fill disks, especially in CI/CD or container builds.
# --- Debian/Ubuntu ---
# Show cache size
du -sh /var/cache/apt/archives/
# 847M /var/cache/apt/archives/
# Clean only packages that can no longer be downloaded
apt autoclean
# Remove ALL cached packages
apt clean
# Remove unused dependencies
apt autoremove -y
# --- RHEL/CentOS/Fedora ---
# Show cache size
du -sh /var/cache/dnf/
# 612M /var/cache/dnf/
# Clean all cached data
dnf clean all
# Clean only metadata
dnf clean metadata
# Clean only packages
dnf clean packages
In Dockerfiles: Always run apt clean && rm -rf /var/lib/apt/lists/* (or dnf clean all) in the same RUN layer as your install to keep image size down:
RUN apt-get update && \
apt-get install -y --no-install-recommends nginx=1.24.0-1ubuntu1 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
10. Troubleshooting Broken Packages¶
Broken packages are inevitable. Here is the triage sequence.
# --- Debian/Ubuntu ---
# Check for broken packages
dpkg --audit
# Fix broken dependencies
apt --fix-broken install
# Reconfigure packages that failed post-install scripts
dpkg --configure -a
# Force remove a completely broken package
dpkg --remove --force-remove-reinstreq <package>
# Check dpkg log for what happened
tail -50 /var/log/dpkg.log
# --- RHEL/CentOS/Fedora ---
# Check for problems
dnf check
# Rebuild the RPM database (nuclear option)
rpm --rebuilddb
# Check transaction history
dnf history
dnf history info 42
# Undo a specific transaction
dnf history undo 42
The 3 AM sequence for a broken dpkg:
1. dpkg --audit — what is broken?
2. apt --fix-broken install — let apt try to resolve it
3. dpkg --configure -a — finish pending configurations
4. If all else fails: dpkg --remove --force-remove-reinstreq <package> then reinstall
5. Check /var/log/dpkg.log and /var/log/apt/history.log for the timeline
Gotcha: rpm --rebuilddb rebuilds the database from headers of installed packages. If the headers are corrupt, you lose package tracking. Take a backup of /var/lib/rpm/ first.
11. Transaction History and Rollback¶
Knowing what changed and being able to undo it is critical for production.
# --- dnf transaction history ---
dnf history
# ID | Command | Date and time | Actions | Altered
# 127 | upgrade --security | 2024-03-14 02:00 | Upgrade | 12
# 126 | install nginx | 2024-03-13 14:22 | Install | 4
dnf history info 127
dnf history undo 127 -y
# --- Debian/Ubuntu ---
# No built-in transaction rollback. Use:
cat /var/log/apt/history.log
# Or parse /var/log/dpkg.log
# Downgrade a specific package
apt install nginx=1.22.0-1ubuntu1
Interview tip: When asked about configuration drift across a fleet, the strong answer connects package management to automation: "Pin versions in your Ansible/Puppet/Chef manifests, use
apt-mark holdordnf versionlockto prevent unattended upgrades from breaking pins, and audit installed packages across hosts withdpkg-query -Worrpm -qapiped through a diff."
Production lesson: dnf history undo is one of the strongest arguments for RHEL-family in production. On Debian, you must track changes externally (Ansible, etckeeper, or your own tooling) because there is no native rollback.
12. Practical Patterns¶
Pattern: Idempotent package install in automation
# Ansible-style check before install (shell script)
ensure_installed() {
local pkg="$1"
if ! dpkg -s "$pkg" &>/dev/null; then
apt-get install -y "$pkg"
fi
}
ensure_installed curl
ensure_installed jq
Pattern: Lock file contention
# apt lock files: /var/lib/dpkg/lock-frontend, /var/lib/apt/lists/lock
# If another process holds the lock:
lsof /var/lib/dpkg/lock-frontend
# Kill the stale process or wait — never rm the lock file directly
# Common in cloud-init race conditions:
# cloud-init runs apt on boot, your automation runs apt at the same time
# Solution: wait for cloud-init to finish
cloud-init status --wait
Pattern: Audit what is installed
# Full installed package list with versions (Debian)
dpkg-query -W -f='${Package}\t${Version}\n' > /tmp/packages-$(hostname).txt
# Full installed package list with versions (Red Hat)
rpm -qa --queryformat '%{NAME}\t%{VERSION}-%{RELEASE}\n' > /tmp/packages-$(hostname).txt
# Compare two hosts
diff <(ssh host1 "dpkg-query -W -f='\${Package}\t\${Version}\n'" | sort) \
<(ssh host2 "dpkg-query -W -f='\${Package}\t\${Version}\n'" | sort)
Wiki Navigation¶
Related Content¶
- Debian & Ubuntu Ecosystem (Topic Pack, L1) — Package Management
- Linux Ops (Topic Pack, L0) — Package Management
- Package Management Flashcards (CLI) (flashcard_deck, L1) — Package Management
- Skillcheck: Linux Fundamentals (Assessment, L0) — Package Management