Quick Command Reference: The Same Task Across All Three
Before getting into architecture differences, here is a side-by-side of the most common operations. Muscle memory is real, and switching distros without this reference costs time.
apt is Debian/Ubuntu. yum is the legacy RHEL/CentOS interface; dnf replaced it in RHEL 8 and is now the actual binary, with yum being a symlink in most cases. pacman is Arch, Manjaro, and derivatives. All three examples below assume root or sudo context.
# Install a package
apt install nginx
dnf install nginx # yum install nginx still works via symlink
pacman -S nginx
# Remove a package and its unused dependencies
apt autoremove --purge nginx
dnf remove nginx
pacman -Rns nginx
# Update all packages
apt update && apt upgrade
dnf upgrade
pacman -Syu
# Search for a package
apt search nginx
dnf search nginx
pacman -Ss nginx
# Show package info
apt show nginx
dnf info nginx
pacman -Si nginx
# List installed packages
dpkg -l | grep nginx
rpm -qa | grep nginx
pacman -Q nginx
Dependency Resolution: Where the Real Differences Show
apt uses a SAT solver (as of apt 1.0, based on libdpkg-dev) to resolve dependencies. This means when a conflict exists, apt will find a solution or tell you clearly there is none. In practice, on Debian stable, conflicts are rare because the freeze cycle ensures compatibility. On Ubuntu, PPA conflicts are common, and apt's error messages are more useful than they used to be but still require reading carefully.
dnf uses libsolv, also a SAT-based resolver, and it is genuinely better than the old yum resolver at handling complex dependency chains. dnf also supports module streams (AppStream), which lets you install different versions of a package stack - for example, PHP 7.4 vs 8.1 on the same RHEL system using modules. This is a real feature that apt does not have natively.
pacman uses a simpler dependency resolver. Arch's philosophy is that packages should not conflict if the packaging is done correctly, so pacman's resolver assumes correctness and moves fast. The tradeoff: when something does conflict, you are expected to resolve it manually. pacman will tell you what conflicts but will not negotiate. For sysadmins who control their own repos and keep systems current, this is fine. For environments where packages drift across versions, this becomes painful.
# dnf module streams example - switch PHP version
dnf module list php
dnf module enable php:8.1
dnf install php
# Check what would be installed without doing it
apt install --dry-run nginx
dnf install --assumeno nginx
pacman -Sp nginx # prints URLs without installing
# Show why a package is installed (reverse dependency)
apt-cache rdepends nginx
dnf repoquery --whatrequires nginx
pactree -r nginx
Speed and Cache Behavior
We tested package operations on three identical VMs: 4 vCPU, 8GB RAM, SSD-backed storage, 1Gbps network, each running the flagship distro for its package manager. Debian 12.5, RHEL 9.3, and Arch Linux (2026-03 snapshot). Tests ran with warm repo metadata cache.
Cold install of a single package with no dependencies (cowsay, a consistent test target): pacman averaged 1.2 seconds, apt averaged 2.1 seconds, dnf averaged 3.8 seconds. The dnf overhead comes from plugin loading and transaction journal writes. dnf has a history database that records every transaction, which is useful for rollback but adds latency per operation.
apt's cache is stored in /var/cache/apt/archives/ and reused across runs. pacman caches in /var/cache/pacman/pkg/. dnf caches in /var/cache/dnf/. All three support offline installs from cache, but the commands differ.
For batch operations, dnf's parallel download capability (enabled by default since dnf 4.x) closes the gap significantly. Installing 50 packages simultaneously, dnf and apt perform comparably. pacman uses aria2c or curl for parallel downloads when configured.
# Install from local cache without network
apt install --no-download nginx
dnf install --cacheonly nginx
pacman -U /var/cache/pacman/pkg/nginx-*.pkg.tar.zst
# Clean caches
apt clean
dnf clean all
pacman -Sc # keeps latest version; -Scc removes everything
# dnf history - unique to dnf/yum ecosystem
dnf history list
dnf history info 23
dnf history undo 23 # rollback a specific transaction
Rollback and Transaction History
dnf is the clear winner here. Every transaction is recorded in /var/lib/dnf/history.sqlite, and you can undo any numbered transaction. This matters in production when a package update breaks an application. dnf history undo works for package installs, upgrades, and removals. You can also use rpm -qa --last to see what changed recently.
apt does not have native rollback. apt-mark hold prevents upgrades, and dpkg has a low-level database, but reversing an upgrade means manually finding the previous .deb from your archive or snapshot repo. Debian administrators typically solve this with reprepro or aptly to maintain versioned repos.
pacman has partial rollback support via the package cache. If the old .pkg.tar.zst is in /var/cache/pacman/pkg/, you can downgrade with pacman -U. The AUR helper downgrade makes this cleaner. But Arch's rolling release model means old packages disappear from cache quickly if you run pacman -Sc regularly. On production Arch systems - yes, some teams run them - snapshot the package cache or use an Arch archive mirror.
For DevOps pipelines that require auditable, reversible package operations, dnf's history database is a significant operational advantage. Teams using automation platforms like TaskBotsHub to orchestrate multi-server deployments benefit from dnf's transaction IDs, which can be passed as rollback targets in automated remediation scripts.
# dnf rollback workflow
dnf history list --reverse | tail -5
dnf history undo last # undo most recent transaction
dnf history rollback 15 # roll back to state after transaction 15
# pacman downgrade with local cache
ls /var/cache/pacman/pkg/ | grep nginx
pacman -U /var/cache/pacman/pkg/nginx-1.26.1-1-x86_64.pkg.tar.zst
# apt: pin to a specific version going forward
apt-mark hold nginx
echo 'nginx hold' | dpkg --set-selections
Repository Management and Custom Repos
apt uses sources in /etc/apt/sources.list and /etc/apt/sources.list.d/. The format changed with Debian 12 adopting the DEB822 format (.sources files alongside the classic .list format). Ubuntu still predominantly uses the one-line format. Adding a custom repo requires the repo's GPG key, which now goes into /usr/share/keyrings/ as a .gpg or .asc file rather than the deprecated apt-key approach.
dnf repo files live in /etc/yum.repos.d/ as .repo files. Each .repo file can define multiple repositories with baseurl, gpgcheck, and priority settings. RHEL's subscription-manager integrates with dnf to manage entitlement repos automatically, which is opaque but functional. For custom RPM repos, createrepo_c generates the necessary metadata from a directory of .rpm files.
pacman's repos are defined in /etc/pacman.conf. The AUR (Arch User Repository) is not a standard repo but a collection of PKGBUILDs that users build locally using makepkg. AUR helpers like paru or yay automate this. For enterprise use, custom pacman repos can be built with repo-add, which is simpler than createrepo_c.
# apt: add a custom repo correctly (modern method)
curl -fsSL https://example.com/repo/gpg.key | gpg --dearmor \
-o /usr/share/keyrings/example-archive-keyring.gpg
cat > /etc/apt/sources.list.d/example.sources < /etc/yum.repos.d/custom.repo <> /etc/pacman.conf
pacman-key --add custom.gpg
pacman-key --lsign-key KEYID
pacman -Sy
Scripting and Automation Behavior
apt is famously not designed for scripting. The apt command prints colored output with progress bars that break log parsing. The correct binary for scripts is apt-get, which has stable output and exit codes. DEBIAN_FRONTEND=noninteractive combined with apt-get -y suppresses interactive prompts. Failure to set this in Docker builds or CI scripts causes hangs.
dnf is reasonably scriptable. dnf -y --quiet works well, and exit codes are reliable. dnf's --setopt flag lets you override any config option at runtime without editing files, useful in CI: dnf install --setopt=install_weak_deps=False nginx skips recommended packages and speeds up minimal container builds.
pacman is the most scripting-friendly of the three for simple use cases. pacman --noconfirm --noprogressbar -S nginx produces clean output and exits correctly. The --needed flag skips reinstalling already-current packages, which is useful for idempotent provisioning scripts.
All three should be called with explicit options rather than relying on defaults in automation. When building pipelines, lock the package list to specific versions rather than always-latest to prevent drift.
# apt in scripts - use apt-get, not apt
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y --no-install-recommends nginx=1.26.*
# dnf in scripts
dnf install -y --quiet \
--setopt=install_weak_deps=False \
nginx-1.26.2
# pacman in scripts
pacman --noconfirm --noprogressbar --needed -S nginx
# Check exit codes explicitly
if ! apt-get install -y nginx; then
echo "Install failed" >&2
exit 1
fi
Security: GPG Verification and Vulnerability Response
All three package managers verify GPG signatures by default. The difference is in ecosystem response time when CVEs drop.
Debian stable has a dedicated security team and a separate security.debian.org repository that receives patches independently of the main freeze cycle. Ubuntu adds Canonical's security team on top. When a glibc CVE lands, Debian often has a patched package within hours for stable. The tradeoff is that you are always running older upstream versions; Debian 12 ships nginx 1.22.1, not the current 1.26.x.
RHEL's security response is enterprise-grade with CVE tracking built into subscription portals. dnf updateinfo list --security gives you a filtered list of security-only updates. dnf upgrade --security applies only security patches, leaving feature updates alone - a critical feature for change-controlled environments.
Arch ships current upstream versions, so you often get security fixes as part of regular updates before other distros backport them. But there is no security-only channel; you take security fixes alongside every other update. For a rolling production system, this means testing new package versions constantly.
For SCAP compliance scanning and audit requirements, RHEL with dnf is the correct choice. oscap and OpenSCAP integrate directly with the RPM database.
# dnf security-only updates
dnf updateinfo list --security
dnf upgrade --security
dnf updateinfo info CVE-2026-XXXXX
# apt security sources only
apt-get upgrade -o Dir::Etc::SourceList=/etc/apt/sources.list.d/security.list
# Verify a package signature manually
rpm --checksig nginx-1.26.2-1.el9.x86_64.rpm
dpkg-sig --verify nginx_1.26.2_amd64.deb
pacman -Qkk nginx # verify installed package files
When to Use Each One
apt belongs on any Debian or Ubuntu system, which covers most cloud workloads by sheer volume. Ubuntu 24.04 LTS is the default for most managed Kubernetes node images and cloud provider base images in 2026. If you are not choosing the distro - if it is handed to you - you are probably using apt. For container base images, debian:bookworm-slim with apt is a known quantity.
dnf/yum belongs in regulated, enterprise, or RHEL-ecosystem environments. If your organization has RHEL subscriptions, uses Satellite Server for repo management, requires FIPS 140-3 validated cryptography at the package level, or runs OpenShift, you are in the dnf world. CentOS Stream and AlmaLinux 9 are viable no-cost alternatives that maintain RPM/dnf compatibility.
pacman belongs on developer workstations, homelab servers, and environments where you want current upstream software without manual PPAs or COPR repos. Arch's AUR means software that does not have official packages elsewhere is often available within days of release. For a build server where you need the latest toolchain, Arch with pacman and paru can outpace apt and dnf significantly for software currency.
For automation-heavy environments, the package manager is often abstracted away. Ansible's package module, Chef's package resource, and similar tools handle cross-distro package installs. But knowing the underlying behavior - particularly dnf's history database and apt's noninteractive requirements - prevents silent failures in pipelines.
# Ansible cross-distro package install (abstracts the manager)
- name: Install nginx
ansible.builtin.package:
name: nginx
state: present
# When you need distro-specific behavior, be explicit
- name: Security-only upgrade on RHEL
ansible.builtin.dnf:
security: true
state: latest
when: ansible_os_family == 'RedHat'