Skip to main content

apt vs yum vs dnf — Linux Package Managers Demystified

· 7 min read
Goel Academy
DevOps & Cloud Learning Hub

You just got a new job. Half the servers run Ubuntu, the other half run CentOS. Your Ansible playbooks need to work on both. Your Dockerfiles target different base images. Which package manager do you use? What are the actual differences? Let's clear this up once and for all.

The Two Families of Linux Packaging

Linux distributions fall into two major packaging families:

FamilyPackage FormatLow-Level ToolHigh-Level ToolDistros
Debian.debdpkgapt / apt-getDebian, Ubuntu, Mint
Red Hat.rpmrpmyum / dnfRHEL, CentOS, Fedora, Rocky, Alma

The low-level tools (dpkg, rpm) handle individual package files. The high-level tools (apt, yum, dnf) handle dependency resolution, repositories, and updates.

apt — The Debian/Ubuntu Way

apt is the modern, user-friendly replacement for apt-get. Use apt for interactive use, apt-get in scripts (it has a more stable output format).

# Update package index (always do this first!)
sudo apt update

# Upgrade all installed packages
sudo apt upgrade -y

# Full upgrade (handles dependency changes)
sudo apt full-upgrade -y

# Install a package
sudo apt install nginx curl vim -y

# Remove a package (keeps config files)
sudo apt remove nginx

# Remove package AND config files
sudo apt purge nginx

# Remove unused dependencies
sudo apt autoremove -y

# Search for a package
apt search "web server"
apt list --installed | grep nginx

# Show package details
apt show nginx

Managing Repositories

# List current repositories
cat /etc/apt/sources.list
ls /etc/apt/sources.list.d/

# Add a third-party repository (example: Docker)
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install docker-ce -y

Package Pinning — Hold a Version

Sometimes you don't want a package to auto-upgrade (looking at you, kernel updates on production servers).

# Hold a package at its current version
sudo apt-mark hold nginx
# nginx set on hold.

# List held packages
apt-mark showhold

# Release the hold
sudo apt-mark unhold nginx

# Install a specific version
apt list -a nginx # See available versions
sudo apt install nginx=1.24.0-1ubuntu1 # Install exact version

yum — The Classic Red Hat Way

yum (Yellowdog Updater Modified) was the default for RHEL/CentOS for years. It works but it's slow because it's written in Python and resolves dependencies sequentially.

# Update package index and upgrade everything
sudo yum update -y

# Install packages
sudo yum install nginx curl vim -y

# Remove a package
sudo yum remove nginx -y

# Search for a package
yum search "web server"
yum list installed | grep nginx

# Show package info
yum info nginx

# List available updates
yum check-update

# Clean cached data
sudo yum clean all

# View history and rollback
yum history list
sudo yum history undo 15 # Undo transaction 15

dnf — The Modern Red Hat Way

dnf (Dandified Yum) is yum's successor. It's faster, has better dependency resolution, and is now the default on Fedora, RHEL 8+, CentOS Stream, Rocky Linux, and AlmaLinux.

# dnf commands mirror yum — easy transition
sudo dnf update -y
sudo dnf install nginx -y
sudo dnf remove nginx -y
sudo dnf search "web server"
dnf info nginx
dnf list installed

# dnf-exclusive features:
# Module streams (multiple versions of the same software)
dnf module list nodejs
sudo dnf module enable nodejs:18
sudo dnf module install nodejs:18

# Automatic updates
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic-install.timer

Managing Repositories (yum/dnf)

# List enabled repositories
dnf repolist
dnf repolist all # Including disabled

# Add EPEL repository (Extra Packages for Enterprise Linux)
sudo dnf install epel-release -y

# Add a custom repo
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo

# Disable a repo temporarily
sudo dnf install somepackage --disablerepo=epel

# Enable a disabled repo temporarily
sudo dnf install somepackage --enablerepo=testing

Version Locking with dnf

# Install the versionlock plugin
sudo dnf install python3-dnf-plugin-versionlock

# Lock a package version
sudo dnf versionlock add nginx

# List locked packages
dnf versionlock list

# Remove a lock
sudo dnf versionlock delete nginx

# Clear all locks
sudo dnf versionlock clear

Head-to-Head Comparison

Here's the same task in all three package managers:

Taskapt (Debian/Ubuntu)yum (RHEL 7/CentOS 7)dnf (RHEL 8+/Fedora)
Update indexapt updateyum check-updatednf check-update
Upgrade allapt upgrade -yyum update -ydnf upgrade -y
Installapt install pkg -yyum install pkg -ydnf install pkg -y
Removeapt remove pkgyum remove pkgdnf remove pkg
Searchapt search termyum search termdnf search term
Infoapt show pkgyum info pkgdnf info pkg
List installedapt list --installedyum list installeddnf list installed
Clean cacheapt cleanyum clean alldnf clean all
History/rollbackN/A (use snapshots)yum history undo Ndnf history undo N
Lock versionapt-mark hold pkgyum versionlock pkgdnf versionlock add pkg

Low-Level Tools — dpkg and rpm

Sometimes you need to work with individual package files directly.

# Debian: dpkg
dpkg -i package.deb # Install a .deb file
dpkg -r package # Remove package
dpkg -l | grep nginx # List installed, filter
dpkg -L nginx # List files installed by package
dpkg -S /usr/sbin/nginx # Which package owns this file?

# Red Hat: rpm
rpm -ivh package.rpm # Install a .rpm file
rpm -e package # Remove package
rpm -qa | grep nginx # List installed, filter
rpm -ql nginx # List files installed by package
rpm -qf /usr/sbin/nginx # Which package owns this file?

Writing Cross-Distro Scripts

In real DevOps work, your scripts need to handle both families. Here's a practical pattern:

#!/bin/bash
# install-tools.sh — Works on Debian and Red Hat families

install_packages() {
if command -v apt &> /dev/null; then
echo "Detected Debian-based system"
sudo apt update
sudo apt install -y curl wget vim htop jq
elif command -v dnf &> /dev/null; then
echo "Detected RHEL 8+ / Fedora"
sudo dnf install -y curl wget vim htop jq
elif command -v yum &> /dev/null; then
echo "Detected RHEL 7 / CentOS 7"
sudo yum install -y curl wget vim htop jq
else
echo "Unsupported package manager" >&2
exit 1
fi
}

install_packages

Dockerfile Best Practices

# Debian/Ubuntu base — clean up apt cache to reduce image size
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
nginx \
&& rm -rf /var/lib/apt/lists/*

# RHEL/Fedora base — clean up dnf cache
FROM fedora:39
RUN dnf install -y \
curl \
nginx \
&& dnf clean all \
&& rm -rf /var/cache/dnf

Key Dockerfile tips:

  • Always combine update and install in one RUN layer
  • Use --no-install-recommends (apt) to skip optional dependencies
  • Clean the cache in the same RUN to keep the layer small
  • Pin versions in production images: nginx=1.24.0-1ubuntu1

When to Use Which?

  • Ubuntu/Debian is dominant in containers, cloud VMs, and developer machines. Use apt.
  • RHEL/Rocky/Alma is dominant in enterprise, banking, and compliance-heavy environments. Use dnf.
  • CentOS 7 still exists in legacy environments. Use yum, but plan your migration.
  • Fedora is great for testing what's coming to RHEL. Use dnf.

The commands are similar enough that switching between them is painless. The biggest adjustment is remembering that apt needs update before install, while dnf handles it automatically.


Next up: Linux Users & Groups — a new developer joins your team. Here's how to set up their Linux access properly.