Module 9 — Package Management
Updated 2 September 2026
Where do programs come from? Not by copying binaries around by hand — by a package manager that installs software with its dependencies, tracks every file it owns, updates it, and removes it cleanly. This module teaches the Debian family (apt/dpkg, what Ubuntu servers run) in depth and the Red Hat family (dnf/rpm) by faithful parallel, so you can work on either fleet an interviewer hands you.
Legend used throughout: 🧠 concept → 🧩 analogy → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (hidden)
You need Modules Module 1 — What Linux Is–Module 8 — Processes — especially sudo and the root model (Module 4: installing changes the whole system, so it needs root), the filesystem layout (Module 2: packages put files in standard places), and pipes/grep (Modules 5–7: you will filter package listings constantly). Tools: an Ubuntu 24.04 machine with sudo. Installing requires working network access to the distro's servers; a few exercises install a tiny, harmless package (tree) and remove it again.
Part A — Why package managers exist
A1. The problem they solve — dependencies
🧠 Almost no program stands alone. A web server needs an SSL library; that library needs a compression library; that needs the C library. Installing software by hand means chasing this chain yourself — the infamous "dependency hell" of the 1990s, where installing one thing meant hunting down twelve others in the right versions. A package manager ends it: you name what you want, and it computes the full dependency tree, fetches every piece from a trusted repository, installs them in the right order, and records exactly what it did so it can be undone. A package is one unit of that system: a compressed archive of files plus metadata — its version, its dependencies, and where its files belong.
Buying a bookshelf that "requires" brackets, screws, and wall anchors, you do not drive to four shops. The store bundles everything, delivers it together, and keeps a record of your order for returns. The package manager is that store: order the bookshelf, receive the whole bill of materials, keep the receipt.
Where the analogy stops working. A furniture store ships you duplicate screws with every order. A package manager is smarter: if the C library is already installed, it is shared, not re-delivered — one copy on disk serves every program that needs it. That sharing is the whole point, and also the whole risk (A2's counter-intuition): removing "your" screws can collapse someone else's shelf.
🧪 Exercise A1.1 — See a dependency chain
apt-cache depends tree # what does the 'tree' package require?
apt-cache depends bash | head -12 # and something more central✅ Expected result — click to reveal
tree
Depends: libc6then bash's longer list (Depends, PreDepends, Recommends, Suggests…).
What to read out of it:
- tree needs only libc6 — the C library, which underpins essentially everything, so it is already present. That is why installing tree is nearly instant: the dependency is shared, not fetched.
- The relationship types matter: Depends (required — won't work without it), Recommends (installed by default, but optional), Suggests (mentioned, never auto-installed). Interviewers probe the Depends/Recommends distinction; it is why --no-install-recommends produces leaner servers.
A2. What "installed" means — the package database
🧠 Installing is not "copying a binary somewhere". The package manager unpacks the files into their standard locations (Module 2's FHS — binaries to /usr/bin, configs to /etc, docs to /usr/share) and writes an entry into a database recording the package's name, version, dependency links, and — crucially — the exact list of files it placed. That database is what makes clean removal, integrity checking, and "which package owns this file?" possible. On Debian systems it lives under /var/lib/dpkg/; dpkg is the low-level tool that reads and writes it.
A library is its books and its catalogue. Shelve a book without cataloguing it and nobody can find it; remove a book but leave its card and the catalogue lies to every patron. The package database is the catalogue; the files are the books; they must change together.
Where the analogy stops working. A librarian notices a missing book when reshelving. The package system has no such background audit by default — a hand-deleted file goes unnoticed until an upgrade or a dpkg -V integrity check stumbles on the gap, often months later, often mid-incident. The catalogue trusts itself completely, which is exactly why you must never let it drift from reality.
🧪 Exercise A2.1 — Interrogate the database
dpkg -l | head -6 # the database header, then installed packages
dpkg -l | wc -l # roughly how many packages make up this system?
dpkg -s bash | head -8 # one package's full record✅ Expected result — click to reveal
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-=======================-====================-============-=================
ii adduser 3.137ubuntu1 all add and remove users and groupsthen a count (often around 1000 on a minimal server, far more on a desktop), then bash's record.
What to read out of it:
- The ii at the start of each row is a status code: first letter = desired state, second = actual state. ii means "installed, and correctly installed" — the healthy normal. A leading rc means "removed, but config files remain"; anything with uppercase letters is a broken state worth investigating. This two-letter column is the first thing to read when a package acts strange.
- A thousand packages compose a "minimal" system — a vivid measure of how much a package manager is quietly tracking. Doing this by hand is unthinkable; that is the point.
- dpkg -s bash shows the metadata: version, dependencies, the Essential: yes flag (packages so fundamental the system guards against their removal). This is the catalogue card, printed.
A3. Which package owns this file?
🧠 The database works both directions. dpkg -S /path/to/file — which package put this file here? dpkg -L packagename — which files did this package place? These answer the two questions that recur endlessly in real work: "where did this mystery binary come from?" and "what exactly will removing this package delete?" (On Red Hat systems: rpm -qf file and rpm -ql package — same two questions, different spelling.)
A land registry answers both "who owns this plot?" (-S) and "what plots does this owner hold?" (-L). Before demolishing anything, you check the register — because the plot you think is abandoned may be load-bearing for a neighbour.
Where the analogy stops working. A registry covers only registered land; squatters are invisible to it. Likewise dpkg -S finds nothing for files not installed by a package — a binary you wget'd into /usr/local/bin, a file a script wrote. The register's silence is meaningful: "no package owns this" is itself the answer, and often the clue.
🧪 Exercise A3.1 — Both directions, then a deliberate blank (last command "fails" on purpose)
dpkg -S $(which ping) # which package owns the ping command?
dpkg -L coreutils | head -8 # what does coreutils own?
dpkg -S /usr/local/bin/madeup 2>&1 # a path no package owns✅ Expected result — a "not found", on purpose — click to reveal
iputils-ping: /usr/bin/ping
/.
/usr
/usr/bin
/usr/bin/[
/usr/bin/arch
/usr/bin/b2sum
/usr/bin/base32
/usr/bin/base64then:
dpkg-query: no path found matching pattern /usr/local/bin/madeupWhat to read out of it:
- The command is ping; the package is iputils-ping — not a package called ping. The lesson: don't assume the package name matches the command name. dpkg -S tells you the truth rather than the guess (some commands do match their package, like sed→sed; many don't).
- dpkg -L coreutils reveals the whole family — arch, base32, b2sum, and dozens more all shipped by one package. This is why "install coreutils" gives you a hundred commands at once.
- The blank on the made-up path is the meaningful negative: no package owns it. On a real box, that answer for a binary in /usr/local/bin says "a human put this here outside the package system" — sometimes fine (locally-built software belongs in /usr/local, Module 2), sometimes the first sign of something that shouldn't be there.
Part A — Interview questions
🎯 "What is dependency management?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)
The package manager's core service: resolving the full graph of libraries and tools a program requires, fetching them from repositories in compatible versions, installing them in dependency order, and sharing common libraries across all packages that need them rather than duplicating. It also prevents removal of a dependency still relied upon, and — via relationship types (Depends, Recommends, Suggests, Conflicts, Breaks) — encodes how strictly each link binds.
The details that separate candidates: the Depends-vs-Recommends distinction and its operational use (--no-install-recommends for lean servers/containers); shared libraries as the reason removal can cascade; and naming the failure mode it replaced ("dependency hell") to show you know why the machinery exists, not just that it does.
🎯 "How do you install software in Linux?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)
Through the distribution's package manager, as root: Debian/Ubuntu sudo apt install name; Red Hat/Fedora sudo dnf install name; the low-level layer is dpkg -i file.deb / rpm -i file.rpm for a local package file (which does not resolve dependencies — that is apt/dnf's job). Always update the package index first on Debian (apt update) so you install current versions from the repository.
The details that separate candidates: the two-layer model (high-level apt/dnf resolve dependencies and talk to repositories; low-level dpkg/rpm handle a single package file) and why installing a raw .deb with dpkg -i can leave you with unmet dependencies to fix; plus the security-relevant point that packages are cryptographically signed by the repository, which is why installing random .deb files off the web is dangerous.
Part B — apt: the everyday workflow
B1. update vs upgrade — the two words everyone confuses
🧠 The single most confused pair in Linux administration. sudo apt update refreshes the index — it downloads the latest catalogue of what versions exist in the repositories; it changes no installed software. sudo apt upgrade installs newer versions of packages you already have, according to that catalogue. So the sequence is always update then upgrade: learn what's available, then apply it. Running upgrade without a recent update upgrades against a stale catalogue — you get yesterday's "latest".
apt update is receiving this season's catalogue in the post — you now know what's newly available, but nothing has arrived. apt upgrade is placing the order and taking delivery. Reading a catalogue changes your shelves not at all; it just makes the ordering accurate.
Where the analogy stops working. A paper catalogue is obviously just paper. apt update's output looks like work — lines scrolling, "Hit", "Get", sizes downloading — so it feels like something changed on your system. It didn't: every one of those lines was fetching list data, not software. The convincing activity is exactly what makes the misconception so durable.
🧪 Exercise B1.1 — Refresh the catalogue, then see what's pending
sudo apt update # refresh the index (downloads lists, changes no software)
apt list --upgradable # what COULD be upgraded now? (no sudo needed — read-only)✅ Expected result — click to reveal
Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
Get:2 http://archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB]
...
Fetched 328 kB in 1s (402 kB/s)
Reading package lists... Done
Building dependency tree... Done
N packages can be upgraded. Run 'apt list --upgradable' to see them.then, from the second command:
Listing...
bzip2/noble-updates,noble-security 1.0.8-5.1ubuntu0.1 amd64 [upgradable from: 1.0.8-5.1build0.1]
ca-certificates/noble-updates,noble-security 20260601~24.04.1 all [upgradable from: 20240203]
...What to read out of it:
- Hit = the list was already current; Get = it fetched a newer list. Sizes are in kilobytes — these are catalogues, not programs. Nothing on your system got newer.
- apt list --upgradable reads the freshly-updated catalogue against what you have installed and shows the gap — the shopping list upgrade would act on. Note [upgradable from: X]: old version → available version, per package.
- The noble-security source on some lines flags security updates specifically — the ones you never defer. Servers often apply only these automatically (unattended-upgrades), a Module 11-adjacent practice worth naming in interviews.
B2. install and remove — and the two kinds of removal
🧠 sudo apt install name fetches the package and its dependencies and installs them; it is idempotent — running it on an already-installed, current package does nothing (a property that makes it safe in automation, Module 10). Removal comes in two strengths: sudo apt remove name deletes the program's files but keeps its config (in /etc); sudo apt purge name deletes the config too. The distinction is deliberate: remove then reinstall preserves your settings; purge is "erase every trace". And sudo apt autoremove sweeps up dependencies that were pulled in for something now removed and are no longer needed by anything — the answer to slow package-cruft accumulation.
apt remove is moving out but leaving your custom shelving and paint (the config in /etc) for when you move back. apt purge is restoring the flat to bare walls — nothing of yours remains. autoremove is the landlord clearing the storage-unit clutter that only existed to serve a tenant who has already left.
Where the analogy stops working. A landlord can see abandoned clutter and judge. autoremove decides purely from the dependency graph — "was this auto-installed, and does anything still Depend on it?" — and it can be startling: removing one small package you no longer want may cascade autoremove into pulling twenty libraries. Always read the "will be REMOVED" list before confirming; the graph's logic is correct but rarely matches intuition.
🧪 Exercise B2.1 — Install, verify, reinstall (idempotent), remove
sudo apt install -y tree # install a small, harmless package (-y auto-confirms)
tree --version # prove it works
dpkg -L tree | grep bin # where did its binary land?
sudo apt install -y tree # run it AGAIN — watch idempotence
sudo apt remove -y tree # remove it (config, if any, stays)✅ Expected result — click to reveal
... Unpacking tree (2.1.1-2ubuntu3.24.04.2) ...
Setting up tree (2.1.1-2ubuntu3.24.04.2) ...
tree v2.1.1 (c) 1996 - 2023 by Steve Baker, ...
/usr/bin/treethen, on the second install:
tree is already the newest version (2.1.1-2ubuntu3.24.04.2).
0 upgraded, 0 newly installed, 0 to remove and 162 not upgraded.What to read out of it:
- First install: Unpacking then Setting up — the two phases (place files, then run the package's configuration step). The binary landed in /usr/bin/tree, exactly where FHS says (Module 2, confirmed by the package system).
- Second install: already the newest version, 0 newly installed — nothing happened, no error. That is idempotence, and it is why apt install is safe to put in a provisioning script that runs a hundred times.
- The summary line's grammar — N upgraded, N newly installed, N to remove, N not upgraded — is worth reading on every apt command; it is apt telling you the blast radius before (and after) it acts.
B3. Searching and inspecting before you install
🧠 apt search term finds packages by name and description; apt show name prints a package's details — version, size, dependencies, description — without installing it; apt-cache policy name shows which version is installed versus available and from which repository. These are the "look before you leap" tools: know what you're about to pull in, from where, at what version, before it touches the system.
apt show is turning the box over in your hands in the aisle: ingredients (dependencies), size, best-before (version), which shelf it came from (repository component). You read it before it goes in the trolley, not after it's in your kitchen.
Where the analogy stops working. A product label is written to persuade you. apt show is the maintainer's own factual record — no marketing, and it reflects exactly the version your machine's current catalogue would install, so the box you read is the box you get. The one gap: the label can't tell you whether you actually need it, which is why the human judgment of "do we want this on a server at all?" still sits with you.
🧪 Exercise B3.1 — Inspect a package you don't have
apt show tree 2>/dev/null | head -12 # details, no installation
apt-cache policy bash # installed vs candidate version✅ Expected result — click to reveal
Package: tree
Version: 2.1.1-2ubuntu3.24.04.2
Priority: optional
Section: universe/utils
...then:
bash:
Installed: 5.2.21-2ubuntu4
Candidate: 5.2.21-2ubuntu4
Version table:
*** 5.2.21-2ubuntu4 500
500 http://archive.ubuntu.com/ubuntu noble/main amd64 Packages
100 /var/lib/dpkg/statusWhat to read out of it:
- apt show answered "what am I about to install?" with zero commitment — section, version, size, dependencies. The Section: universe/... is Ubuntu's repository areas: main (Canonical-supported), universe (community-maintained), restricted/multiverse (licensing caveats). Which area a package lives in is a real support-and-security consideration on servers.
- apt-cache policy shows Installed and Candidate (what an upgrade would install) side by side — here identical, so bash is current. When they differ, that gap is exactly what apt upgrade would close. The 500/100 numbers are pin priorities — how apt chooses among multiple available versions; you rarely touch them, but recognizing them prevents confusion when troubleshooting "why did it pick that version?"
Part B — Interview questions
🎯 "How do you update installed packages?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)
Debian/Ubuntu: sudo apt update (refresh the index) then sudo apt upgrade (install newer versions) — the two are distinct and ordered. apt full-upgrade (or the older dist-upgrade) additionally allows removing packages when needed to complete an upgrade. Red Hat/Fedora: sudo dnf upgrade (which refreshes metadata itself, so no separate update step). For security-only patching, servers often run unattended-upgrades.
The details that separate candidates: the update/upgrade distinction stated crisply (update = catalogue, upgrade = software) — the classic tell of hands-on experience; upgrade vs full-upgrade (the latter can remove packages, so it's not the default); and the operational nuance that production servers stage and test upgrades rather than running apt upgrade blindly, applying security updates promptly but feature updates on a schedule.
🎯 "How do you remove a package?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)
sudo apt remove name (deletes files, keeps config), sudo apt purge name (deletes config too), sudo apt autoremove (sweeps orphaned dependencies) — Red Hat: sudo dnf remove name, with dnf autoremove. Always read apt's "the following packages will be REMOVED" list before confirming, because removal can cascade to dependents.
The details that separate candidates: the remove-vs-purge distinction and when each is right (remove to keep settings for a reinstall; purge to erase config, e.g. before reconfiguring from scratch); knowing autoremove decides purely from the dependency graph and can surprise; and the discipline of confirming the blast-radius list on production rather than reflexive -y.
Part C — Repositories, and the low-level layer
C1. Where packages come from — repositories and trust
🧠 A repository is a server holding thousands of packages plus a signed index. Your machine's list of trusted repositories lives in /etc/apt/sources.list and /etc/apt/sources.list.d/* (modern Ubuntu uses the .sources format there). Every package is cryptographically signed; apt verifies the signature against keys the system trusts before installing anything — which is why you cannot silently be served a malicious package by a man-in-the-middle, and why adding a third-party repository means adding its signing key (an explicit act of extending trust). This signing chain is the security backbone of the whole system.
Repositories are licensed distributors: their goods are sealed, tamper-evident (signatures), traceable to the manufacturer, and returnable. curl | sudo bash is buying from an unmarked van in a car park — maybe genuine, maybe not, no seal, no receipt, no recourse, and you've handed over the keys to your house to unpack it.
Where the analogy stops working. A dodgy purchase from a van harms only you. Root-running an unverified script harms the whole machine and everything it can reach — credentials, other services, the network. And unlike a physical fake, malicious code leaves no obvious trace and may act weeks later. The stakes are categorically higher than the analogy's petty risk.
🧪 Exercise C1.1 — Read your machine's sources of trust
ls /etc/apt/sources.list.d/ # the repository definition files
cat /etc/apt/sources.list.d/ubuntu.sources 2>/dev/null | head -12 # the main Ubuntu sources✅ Expected result — click to reveal
ubuntu.sources(possibly with extras like docker.list if third-party repos were added), then:
## Ubuntu distribution repository
Types: deb
URIs: http://archive.ubuntu.com/ubuntu
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpgWhat to read out of it (files and URLs vary by machine and mirror):
- URIs is where packages come from; Suites names the release (noble) and its update channels; Components lists the areas from B3 (main/universe/…). This one file is the machine's definition of "trusted software source".
- Signed-By points at the key that must have signed the index — the trust anchor made concrete. A third-party repo file adds its own Signed-By key, which is the explicit trust extension the counter-intuition warned about.
- Any extra file in sources.list.d/ is a repo someone added deliberately. On a security review, these are exactly what you audit: who added it, is its key legitimate, is it still needed?
C2. dpkg and rpm — the layer beneath
🧠 apt is the high-level tool: it talks to repositories, resolves dependencies, and orchestrates. Underneath, dpkg does the actual work on a single package file and knows nothing of repositories or dependency resolution. The everyday consequence: sudo dpkg -i package.deb installs a local .deb you downloaded — but if it has unmet dependencies, dpkg stops with an error (it cannot fetch them), and the fix is sudo apt install -f (or apt install ./package.deb, which routes the local file through apt so dependencies do resolve). The same two-layer split exists on Red Hat: dnf/yum high-level, rpm low-level.
apt is the general contractor: surveys the whole job, orders materials from suppliers (repositories), schedules the trades in order. dpkg is the specialist who installs the one fixture you hand them — expertly, but they don't order materials or check whether the wiring behind the wall exists. Hand them a fixture needing wiring that isn't there, and they down tools and tell you.
Where the analogy stops working. A specialist tradesperson will improvise or phone a supplier. dpkg will not: it has no supplier's number (no repository access) by design, so a missing dependency is a hard stop, not a workaround. That rigid separation is a feature — it keeps the low-level tool simple and predictable — but it means "just install this .deb" can leave a half-installed system needing apt install -f to reconcile.
🧪 Exercise C2.1 — Query with the low-level tool
dpkg --version | head -1 # confirm the tool and its version
dpkg -l tree 2>/dev/null | tail -2 # dpkg's own view of one package (install tree first if removed)
dpkg -s coreutils | grep -E '^Version|^Pre-Depends' | head -3✅ Expected result — click to reveal
Debian 'dpkg' package management program version 1.22.6 (amd64).then dpkg's package line and coreutils' version and dependency fields (coreutils uses Pre-Depends — dependencies that must be fully configured before it even unpacks — rather than plain Depends; both are dependency relationships, and grepping the wrong one is why the first field can come back alone).
What to read out of it:
- dpkg and apt read the same database — dpkg -l, dpkg -s, dpkg -S, dpkg -L are the query verbs you'll actually use daily, even on machines where you install exclusively through apt. High-level for changes, low-level for questions is a reasonable working split.
- On a Red Hat box every query here has a twin: rpm -q, rpm -qi, rpm -qf, rpm -ql. Learning the concepts — database, ownership queries, the high/low split — means the second family costs you an afternoon of spelling differences, not a re-learning. That transferability is the real Part D lesson.
C3. Building the muscle memory across families
🧠 You will meet both families in a career; the concepts map one-to-one, so a translation table is most of what you need. Read it as "the operation is the same; the spelling differs":
| Task | Debian / Ubuntu (apt · dpkg) | Red Hat / Fedora (dnf · rpm) |
|---|---|---|
| Refresh index | apt update | (automatic; dnf check-update) |
| Install | apt install name | dnf install name |
| Upgrade all | apt upgrade | dnf upgrade |
| Remove | apt remove / purge name | dnf remove name |
| Search | apt search term | dnf search term |
| Show info | apt show name | dnf info name |
| Which package owns a file | dpkg -S /path | rpm -qf /path |
| List a package's files | dpkg -L name | rpm -ql name |
| Install a local package file | apt install ./file.deb | dnf install ./file.rpm |
| List installed | dpkg -l / apt list --installed | rpm -qa / dnf list installed |
🧪 Exercise C3.1 — Translate a task in your head, then run the Debian half
# Task: "what package owns the top command, and what else does that package ship?"
dpkg -S $(which top)
dpkg -L $(dpkg -S $(which top) | cut -d: -f1) | grep -E '/(ps|top|free|pgrep|pkill|uptime)$'
# On a Red Hat box this would be: rpm -qf $(which top); rpm -ql <that package> | grep bin✅ Expected result — click to reveal
procps: /usr/bin/top
/usr/bin/free
/usr/bin/pgrep
/usr/bin/ps
/usr/bin/top
/usr/bin/uptime
/usr/bin/pkillWhat to read out of it:
- top is shipped by procps — alongside ps, free, pgrep, pkill, uptime, and the rest of Module 8's process-and-memory toolkit. One package, the whole kit; now you know what "install procps" gives a stripped-down container (many minimal images omit it, which is exactly why ps sometimes "doesn't exist" inside a container — Module 16).
- The commented Red Hat lines are the same investigation, retyped in the other dialect. Run the Debian half; read the rpm half; feel how little actually differs. That feeling — concepts transfer, spellings don't matter much — is what makes you portable across the two fleets an employer might run.
Part C — Interview questions
🎯 "What is YUM? How does YUM handle package dependencies in Linux?" — asked verbatim in Turing's 100+ Linux interview questions (2025)
YUM (Yellowdog Updater, Modified) is the traditional high-level package manager of the Red Hat family — repository-aware, dependency-resolving — layered over the low-level rpm. It reads enabled repositories, computes the full dependency graph for a requested package, downloads and installs everything in order, and refuses actions that would break dependencies. Its modern successor is DNF (yum is now largely a symlink to dnf on current Fedora/RHEL), faster and with cleaner dependency solving, same command surface.
The details that separate candidates: the yum→dnf evolution (naming yum without noting it's superseded dates you); the high-level(yum/dnf)-over-low-level(rpm) split mirroring apt-over-dpkg; and mapping it to Debian — "yum:rpm as apt:dpkg" — which shows you think in concepts that transfer, not memorized commands for one distro.
🎯 Corpus note — apt vs apt-get, and repositories
The published banks are notably thin on package management (four questions in GeeksforGeeks, one in Turing, none in several others) — so this is a topic where live questions outrun the corpus. Two you should be ready for that the banks omit: "apt vs apt-get?" — apt is the newer, friendlier front-end meant for interactive use (progress bars, colour, sensible defaults); apt-get is the older, stable-interface tool preferred in scripts precisely because its output and flags never change. And "how do you add a third-party repository safely?" — add the repo's signing key to a keyring, add a .sources/.list entry referencing that key with Signed-By, then apt update; never curl | sudo bash. Being fluent where the corpus is silent is, again, a differentiator rather than a gap.
Part D — When packages go wrong
D1. Held and pinned packages — deliberately frozen
🧠 Sometimes you don't want a package upgraded — a database at a version your app is certified against, a kernel you daren't change on a fragile box. sudo apt-mark hold name freezes it: apt upgrade will skip it until you apt-mark unhold. apt-mark showhold lists what's frozen. This is a first-class operations tool, but it has a shadow: a forgotten hold silently blocks security updates for that package, which is how a "why is this one package three years out of date?" audit finding is born.
A held package is machinery wearing a maintenance lockout tag: "do not touch — certified as-is". Right and necessary while a reason holds. The danger is the tag nobody removes: months later a technician skips it on every service round, and the machine quietly ages past every safety update.
Where the analogy stops working. A physical tag is visible on the machine; anyone doing maintenance sees it and can ask why. A hold is invisible unless you run apt-mark showhold — apt upgrade just silently skips the package with no fanfare. The information that would prompt "should this still be held?" is hidden exactly where nobody looks, which is why auditing holds is a real periodic task.
🧪 Exercise D1.1 — Freeze and thaw
sudo apt install -y tree
sudo apt-mark hold tree # freeze it
apt-mark showhold # confirm the freeze
sudo apt-mark unhold tree # thaw it again
apt-mark showhold # empty now (of tree)
sudo apt remove -y tree✅ Expected result — click to reveal
tree set on hold.
tree
Canceled hold on tree.(the final showhold prints nothing for tree)
What to read out of it:
- apt-mark showhold is the audit command: run it on any server and read the list critically — every held package needs a current reason. An unexplained hold is a finding.
- The real-world version of this exercise is a kernel or a database frozen for a genuine compatibility reason, with a ticket recording why and when to revisit — the tag with a date on it, not the tag everyone forgot.
D2. Integrity and the "haunted system"
🧠 A2's counter-intuition — never rm a packaged file — now gets its diagnostic and its cure. dpkg -V (verify) compares installed files against the database's record and reports discrepancies: missing for a deleted file, checksum mismatches for a modified one. When you find (or cause) the drift, the fix is sudo apt install --reinstall name — it re-fetches the package and lays its files back down exactly, reconciling disk with database. On Red Hat, rpm -V package verifies and dnf reinstall repairs.
A warehouse whose computer says "500 units in bay 12" while bay 12 is empty is not a warehouse with 500 units — it is a warehouse with a lying inventory, which is worse, because every downstream decision (don't reorder, promise delivery) trusts the number. dpkg -V is the stock-take that walks the shelves and flags the lies; --reinstall is restocking to match the record.
Where the analogy stops working. A warehouse does periodic stock-takes as routine. Nothing runs dpkg -V automatically — the drift can persist indefinitely, silent, until an upgrade touches the missing file and fails in a confusing way mid-change. The stock-take here is something you must remember to run when a system behaves as if bewitched.
🧪 Exercise D2.1 — Break it, diagnose it, heal it (deliberately creating a broken state)
sudo apt install -y tree
tree --version >/dev/null # run it once (so the shell caches its path — Module 1's 'hashed')
sudo rm /usr/bin/tree # simulate the classic mistake: rm a packaged file
tree --version 2>&1 # the command is "gone"...
dpkg -l tree | tail -1 # ...but the database still says ii (installed)!
dpkg -L tree | grep '/usr/bin/tree' # the db still claims to own the vanished file
sudo apt install --reinstall -y tree # the cure: relay the files from the package
tree --version # healed
sudo apt remove -y tree✅ Expected result — click to reveal
bash: /usr/bin/tree: No such file or directory
ii tree 2.1.1-2ubuntu3.24.04.2 amd64 ...
/usr/bin/tree
Setting up tree (2.1.1-2ubuntu3.24.04.2) ...
tree v2.1.1 (c) 1996 - 2023 by Steve Baker, ...What to read out of it:
- The three-line heart of the lesson: the command is gone — bash: /usr/bin/tree: No such file or directory (because you ran it first, the shell had cached the path; in a fresh shell you'd instead see tree: command not found) — yet dpkg -l still proudly shows ii, and dpkg -L still lists the file it no longer has. The database is confidently wrong — the haunting, reproduced in one rm.
- --reinstall re-laid the exact files and the command returned. It is the correct, surgical repair — far better than purge-and-reinstall, which would also lose config. When a packaged command "disappears", this is the first thing to try.
- The deeper takeaway is preventive: this is why Module 2 said never rm system binaries, and why locally-built or downloaded software goes in /usr/local (unowned by any package) — so a stray deletion there can't desynchronize the package database at all.
D3. Cleaning up, and reading the leftovers
A managed property's garage fills with leftovers: boxes a tenant left behind but might return for (rc config-retained packages), spare parts ordered for appliances long gone (autoremove's orphaned deps), and the stack of delivery cartons everything arrived in (/var/cache/apt downloads). Each is harmless alone; together they eat the space you need.
Where the analogy stops working. A landlord eyeballs the garage and judges what's junk. The package system won't tidy on its own — nothing runs autoremove or apt clean for you — and it distinguishes the three piles by strict rules, not sentiment: a cached .deb is always safe to bin, but a config-retained package might hold settings you'll want, so "clean everything" is a decision, not a reflex.
🧠 Two kinds of cruft accumulate. Orphaned dependencies — libraries auto-installed for packages since removed — cleared by sudo apt autoremove. Cached .deb files — apt keeps every downloaded package under /var/cache/apt/archives/, which can grow to gigabytes; sudo apt clean empties it, apt autoclean removes only the obsolete ones. And the rc state from A2.1 — packages removed but whose config remains — is cleared by purging: dpkg -l | grep '^rc' finds them, sudo apt purge name finishes the job. On a disk-full incident (Module 12), /var/cache/apt is one of the first easy wins.
🧪 Exercise D3.1 — Survey the leftovers
dpkg -l | grep '^rc' | head # packages removed but config-retained (may be empty)
du -sh /var/cache/apt/archives 2>/dev/null # how big is the download cache? (du: Module 12)
sudo apt autoremove --dry-run | tail -5 # what WOULD autoremove clear? (dry run — nothing deleted)✅ Expected result — click to reveal
(rc lines, if any — often none on a fresh machine)
73M /var/cache/apt/archives
...
0 upgraded, 0 newly installed, 0 to remove and 162 not upgraded.What to read out of it:
- --dry-run (apt honours it on most subcommands) is the package world's "show me before you do it" — the same dry-run discipline as Module 6's find and Module 7's sed, applied to system changes. On production, --dry-run first is not optional.
- The cache size is real reclaimable space — on long-lived servers this quietly reaches gigabytes. apt clean is a safe, instant win when df (Module 12) shows /var tight.
- Any rc packages are harmless but untidy — old config for software long gone. A periodic apt purge of them is standard hygiene, and the ^rc grep is exactly how you find the list to feed it.
Part D — Interview questions
🎯 Corpus note — troubleshooting and holds
The published banks essentially do not cover package troubleshooting — held packages, --reinstall, dpkg -V, cache cleanup, the rc state — yet these are exactly what a working sysadmin does when packages misbehave, and exactly what a scenario interview probes ("a command vanished but apt says it's installed — what happened?"). Prepare the three signature repairs cold: haunted/deleted file → apt install --reinstall; half-installed after a bad dpkg -i → apt install -f; package stuck out of date → check apt-mark showhold. Naming the state correctly (database-vs-disk drift, unmet-dependencies, held) before naming the fix is what marks the answer as experience rather than recall.
🎯 A synthesis the banks imply but never ask directly — "walk me through installing, verifying, and cleanly removing a piece of software, and how you'd know each step worked."
Update the index (apt update), inspect before committing (apt show name, read the section and dependencies), install (apt install name — read the "will be installed" list and the newly installed summary), verify it landed (which name, dpkg -L name, run it), and when done, remove cleanly (apt remove to keep config or purge to erase it, then autoremove for orphaned deps) — confirming with dpkg -l name that the state is now rc or absent. Each step has an evidence command, not just an action command.
The details that separate candidates: pairing every mutating command with a verification (the whole spirit of this track); reading apt's blast-radius summaries rather than blindly -y; and knowing the difference between remove, purge, and autoremove precisely enough to pick the right one for "reinstalling later" versus "gone for good".
Part E — Toolkit
E1. Production practice — symptoms and fixes
| Symptom | What is really happening | What to run | The fix |
|---|---|---|---|
| apt upgrade shows nothing new, but you know updates exist | Stale index — you never ran apt update | sudo apt update then apt list --upgradable | update before upgrade, always |
| dpkg -i pkg.deb failed with dependency errors, system now half-installed | Low-level dpkg can't fetch dependencies | sudo apt install -f (fix broken) | Or install via apt install ./pkg.deb so apt resolves deps in the first place |
| A command vanished but dpkg -l says the package is installed | Someone rm'd a packaged file — database/disk drift | dpkg -V name (shows missing) | sudo apt install --reinstall name |
| One package stuck years out of date, security scan flags it | A forgotten apt-mark hold | apt-mark showhold | sudo apt-mark unhold name, then upgrade — after confirming why it was held |
| /var filling up, no obvious culprit | apt's .deb download cache grew for months | du -sh /var/cache/apt/archives | sudo apt clean (safe, instant); then hunt logs (Module 12) |
| Vendor doc says curl https://… | sudo bash | Unsigned, untracked, unremovable root code from a URL | Check for the vendor's real repository first | Add their signed repo; or download, read, and verify the script before running |
| Fleet hosts drifted to different package versions | Ad-hoc apt install per host instead of declared state | Inventory versions (dpkg -l name across hosts) | Configuration management with pinned versions (Ansible track) |
E2. Capstone — four tickets
🎫 Ticket 1 — "Vendor wants us to curl | sudo bash their agent onto every server. Security said ask you."
Ticket text: "Monitoring vendor's install doc is a single curl https://get.vendor.io | sudo bash. Ops wants it on 200 hosts. Is this OK? If not, what do we do instead?"
Worked answer: not OK as-is, and say why concretely — that command runs unsigned code as root from a URL, tracked by no package database (so it can't be cleanly updated or removed), and a compromise of that URL compromises 200 machines at once; it discards every guarantee (signing, versioning, removability, audit) the package system exists to provide. The professional path: (1) check whether the vendor publishes an apt/dnf repository — serious vendors do — and if so, add their signing key to a keyring, add a Signed-By sources entry, and install their package normally, so it's versioned and removable and flows through config management; (2) if they truly only ship a script, at minimum curl it to a file, read it, pin its checksum, and run it under review — never pipe a live URL into root; (3) whichever path, the rollout is via the fleet tool with a pinned version, not a hand-run loop. Frame it for security as: convert an untrusted one-shot into a signed, tracked, reversible install.
🎫 Ticket 2 — "A command disappeared on vm-db-3 but apt insists the package is installed."
Ticket text: "pg_dump reports command-not-found on vm-db-3. apt list --installed | grep postgresql-client shows it installed. We didn't remove anything. Diagnose and fix without disrupting the database."
Worked answer: this is database/disk drift — something deleted the file while the package record survived (a stray rm, a botched manual cleanup, a broken half-upgrade). Confirm: dpkg -S $(which pg_dump) or, since it's missing, dpkg -L postgresql-client | grep pg_dump to get the expected path, then dpkg -V postgresql-client — expect missing /usr/bin/pg_dump. The surgical fix is sudo apt install --reinstall postgresql-client: it re-lays the package's files exactly, touches no config, and does not restart or disrupt the running database (client tools are separate from the server package). Verify with pg_dump --version. Then the real work: find out how the file vanished — check shell history, recent apt/dpkg logs in /var/log/apt/, and cron/cleanup jobs — because a system that deletes packaged files will do it again.
🎫 Ticket 3 — "Security audit: this box hasn't patched openssl in two years. Why, and fix it."
Ticket text: "Compliance scan on vm-legacy-1 shows openssl frozen at a two-year-old version while everything else is current. Explain how one package stays behind, and remediate safely."
Worked answer: one package staying behind while others advance is almost always a hold. Confirm: apt-mark showhold — expect openssl (and possibly libssl) in the list. Now the judgment, not just the command: why was it held? Search change tickets and /var/log for the reason — a hold on a crypto library usually means some fragile app was certified against that version. Remediate safely: don't just unhold-and-upgrade blind on a production box. Stage it — reproduce the app on a test host, apt-mark unhold openssl && apt update && apt install openssl there, run the app's test suite, and only then schedule the same on production with a rollback plan (the old .deb is in /var/cache/apt/archives or downloadable). Document the new state and remove the stale hold's ticket. The audit finding is real; the fix is careful, because the hold existed for a reason that may still bite.
🎫 Ticket 4 — "New starter's laptop: how do we make sure they can build our project — reproducibly?"
Ticket text: "Every new engineer spends a day chasing 'missing library' errors setting up our build. Give us a reproducible way to install the exact toolchain, and explain why ad-hoc apt-installing hasn't worked."
Worked answer: ad-hoc apt install per person drifts — people install different versions on different days, skip a Recommends here, add an extra there, and "works on my machine" is born. The reproducible answer has two parts. First, declare the dependency set: a single list of packages (ideally with pinned versions for the ones that matter) installed in one command — sudo apt update && sudo apt install -y build-essential libfoo-dev=1.2.3 … — kept in the repo, so everyone installs the same set, and apt install being idempotent means re-running it is safe. Second, for true reproducibility across OS versions, put that same list in a container image (Module 16) or a config-management role (Ansible track) so the build environment is defined as code, not folklore. Explain the through-line: the package manager guarantees a correct install; reproducibility comes from declaring what to install once and applying it everywhere, rather than each person improvising.
E3. Documentation reference
| Topic | Authoritative source | Verified link |
|---|---|---|
| High-level Debian/Ubuntu | apt(8), apt-get(8) | apt(8) · apt-get(8) |
| Low-level Debian | dpkg(1) | dpkg(1) |
| Red Hat family | dnf command reference, rpm(8) | dnf command reference · rpm(8) |
| Ubuntu package management guide | Ubuntu Server docs | Ubuntu package management |
| Debian/Ubuntu apt (bookworm reference) | Debian manpages | apt(8) — Debian |
E4. Self-assessment
Answer out loud, without notes. The section number tells you where to re-read.
- What problem does a package manager solve that hand-copying binaries does not — and what does "dependency hell" name? (A1)
- What does "installed" actually mean, and why must you never rm a packaged file? (A2, D2)
- dpkg -S and dpkg -L — which question does each answer, and what does a blank result from -S tell you? (A3)
- apt update versus apt upgrade: what does each do, why the order, and what does update change on disk? (B1)
- remove vs purge vs autoremove — pick the right one for "reinstalling later", "gone for good", and "clean up orphaned deps". (B2)
- Why is apt install safe to put in a script that runs a hundred times? (B2)
- What is a repository, what makes a package trustworthy, and why is curl | sudo bash the opposite? (C1)
- apt vs dpkg (and dnf vs rpm): which layer resolves dependencies, and what happens when the low-level tool meets an unmet dependency? (C2)
- Translate to Red Hat: install nginx; find which package owns /usr/sbin/nginx; list nginx's files. (C3)
- A package is stuck out of date while others upgrade. First command, and the judgment before you unhold. (D1)
- A command vanished but dpkg -l says ii. Name the state, the diagnostic, and the repair. (D2)
- Two kinds of package cruft and the command that clears each; where does the .deb cache live? (D3)
E5. Sources
GeeksforGeeks — Linux Interview Questions (70+) (updated July 2026 — the four package-management questions) · Turing — 100+ Linux Interview Questions (2025 — the YUM question).
Corpus honesty note: package management is thinly covered in the published banks — four dedicated questions in GeeksforGeeks, one in Turing, and none in InterviewBit, Adaface, WeCreateProblems, or Mindmajix among the sets surveyed. This is a genuine gap between the printed corpus and the job: package operations are daily work and a staple of scenario interviews ("a command disappeared…", "how would you roll this out to the fleet?"). Where this module marks a "corpus note", that is the honest state of the record, and the material is taught to the live format the banks miss.
All documentation links on this page were fetched and confirmed reachable on 2 September 2026 (apt/apt-get via the Ubuntu and Debian manpage archives, since these tools are not on man7.org).