You will install, query, pin, and add repositories for software using apt on Debian/Ubuntu and dnf on RHEL/Fedora. By the end you can keep servers patched, audit what is installed, and add third-party repos safely.

Learning Objectives

  • Install, remove, and search packages on both package managers.
  • Add a third-party repository with a verified signing key.
  • Pin a package version and audit installed software.
  • Time: ~2 hours · Difficulty: Beginner · Prereqs: a Debian-family and/or RHEL-family server.

Architecture Overview

Environment Setup

You will need: a server with apt or dnf and internet access.

Before you begin: know which family you are on (cat /etc/os-release).

Prerequisites

  • A Debian/Ubuntu host for apt and/or a RHEL/Fedora host for dnf, with internet access.
  • Root or sudo access for installs and repo changes.
  • Know your distro family: cat /etc/os-release.
  • No extra tools needed; apt and dnf are preinstalled.

Step-by-Step Execution

01
Install and search (Debian family)

apt resolves dependencies automatically; searching helps find the right package name.

sudo apt update && apt search nginx && sudo apt install -y nginx
[ROOT REQUIRED] Updates metadata and installs nginx.

Expected output:

Setting up nginx (1.24.0-2ubuntu7) ...
Processing triggers for man-db ...
(nginx installed)

What this teaches: apt update refreshes metadata before any install; search finds the right package name; -y automates confirmation for scripts.

02
Equivalent on RHEL family
sudo dnf install -y nginx && dnf info nginx

Expected output:

Installed:
  nginx-1:1.24.0-1.el9.x86_64
Complete!

What this teaches: dnf is the RHEL-family counterpart: install resolves dependencies identically, and info shows version, repo, and description.

03
Add a third-party repo with a verified key

Verifying the signing key is what makes adding an external repo safe rather than risky.

curl -fsSL https://example.com/key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/example.gpg

Expected output:

(key imported silently; verify with: ls -l /usr/share/keyrings/example.gpg)

What this teaches: Dearmoring the key into /usr/share/keyrings and referencing it with signed-by keeps the repo trust scoped instead of global. Importing the key alone does not add the repo; the sources entry comes next.

echo "deb [arch=amd64 signed-by=/usr/share/keyrings/example.gpg] https://example.com/apt stable main" | sudo tee /etc/apt/sources.list.d/example.list && sudo apt update
[ROOT REQUIRED] Registers the repo, scoped to its own key, and refreshes metadata.

Expected output:

deb [arch=amd64 signed-by=/usr/share/keyrings/example.gpg] https://example.com/apt stable main
Hit:1 https://example.com/apt stable InRelease

What this teaches: The deb line binds the repo URL to the key you just imported via signed-by, so a compromised third-party key can only sign this one repo. On RHEL-family systems the equivalent is a .repo file under /etc/yum.repos.d/ with gpgcheck=1 and a gpgkey= URL.

04
Audit what is installed

Expected output:

$ apt list --installed 2>/dev/null | wc -l
734
$ # review with: apt list --installed | grep -i nginx

What this teaches: Listing installed packages and counting them turns 'what is on this box' from a guess into an auditable number you can diff over time. Note: the count includes apt list's 'Listing... Done' header line; dpkg-query -W -f='${binary:Package}\n' | wc -l gives a clean count with no header.

05
Pin a package version

Pinning holds a critical package at its current version across upgrades, so unattended updates cannot move it unexpectedly.

sudo apt-mark hold nginx && apt-mark showhold
[ROOT REQUIRED] Freezes nginx at its installed version.

Expected output:

nginx set on hold.
nginx

What this teaches: apt-mark hold is the quick freeze; for version-specific pins, write a Package/Pin/Pin-Priority stanza in /etc/apt/preferences.d/ instead. On RHEL the equivalent is sudo dnf versionlock add nginx (from the dnf-plugins-core package). Release with apt-mark unhold when you are ready to move.

How It Works

Package managers solve dependency resolution: given a desired package, they compute the closure of required libraries, fetch consistent versions from signed repositories, and install them transactionally. apt (with dpkg underneath) and dnf (with rpm underneath) differ in commands but share this model, which is why skills transfer between families.

Repository trust is cryptographic. Each repo signs its metadata with a GPG key; the manager verifies signatures before trusting anything. Adding a third-party repo therefore means adding its key, and the modern practice scopes that key to one repo via signed-by instead of the old global apt-key trust-everything approach.

Pinning and auditing are the fleet disciplines. Pinning (apt preferences, dnf versionlock) holds critical packages at known versions across upgrades; auditing (apt list --installed, rpm -qa) inventories the system so vulnerability scans and compliance checks have ground truth.

Progress So Far

Testing & Validation

nginx -v && systemctl is-enabled nginx

You should see the installed version and that the service is enabled. If so, package management is working end to end.

Troubleshooting

SymptomLikely causeFix
NO_PUBKEY errors on apt updateRepo key missing from the keyringImport the key to /usr/share/keyrings and reference it with signed-by
Held or broken packages block upgradesConflicting version constraintsRun apt --fix-broken install, then review holds with apt-mark showhold
dnf reports nothing provides XRepo not enabled or package named differentlyEnable EPEL/CRB as needed and search with dnf provides
Wrong version installed after adding a repoRepo priority lower than the basePin with apt preferences or dnf versionlock to control selection
apt update is very slowSlow or distant mirrorSwitch mirrors via software-properties or edit sources.list
Accidental dist-upgrade breakageMajor release jump without preparationSnapshot first, read release notes, prefer full-upgrade deliberately
Troubleshooting
  • NO_PUBKEY error: import the repo's signing key into a keyring before updating.
  • Held/broken packages: run sudo apt --fix-broken install.
  • Wrong version installed: pin with apt preferences or dnf versionlock.

Extension Ideas

  • Automate patching with unattended-upgrades.
  • Build a local mirror or caching proxy (apt-cacher-ng) for many hosts.
  • Compare native packages with Snap/Flatpak trade-offs.

Key Results

  • Installed and verified software on both apt and dnf systems.
  • Added a third-party repo with a verified GPG key, no insecure trust.
  • Audited 700+ installed packages for review.
  • Established version pinning to prevent unwanted upgrades.

Key Takeaways

  • apt update before apt install; dnf mirrors the workflow on RHEL.
  • Scope third-party repo keys with signed-by, never global trust.
  • Pin critical packages to survive unattended upgrades.
  • Audit installed packages regularly; inventory is security ground truth.
  • Search before installing; the right package name is half the battle.

Practice Challenges

Challenge 1: Repo add drill

Goal: add a real third-party repo (e.g. Docker or NodeSource) with its key properly scoped, install one package, then remove both cleanly.

Hint: signed-by in the sources line.

Challenge 2: Version hold

Goal: pin nginx to its current version, run a full upgrade, and prove nginx did not move.

Hint: apt-mark hold versus a preferences file; dnf versionlock on RHEL.