Package Management — apt, dpkg & Repositories

How software actually gets onto a Linux box — and how to keep it updated without breaking things.

The Mental Model

Linux doesn't download installers from random websites. Software lives in repositories — signed, versioned collections of packages hosted by your distro. The package manager (apt on Debian/Ubuntu) resolves dependencies, verifies signatures, installs files to standard locations, and tracks every file it placed so removal is clean. One tool installs, updates, and removes everything.

# The daily four
sudo apt update              → refresh the package index (metadata only)
sudo apt upgrade             → install available updates
sudo apt install nginx       → install a package + dependencies
sudo apt remove nginx        → uninstall (keeps config files)

# Slightly less daily
sudo apt purge nginx         → uninstall INCLUDING config files
sudo apt autoremove          → clean up orphaned dependencies
apt search "reverse proxy"   → find packages by keyword
apt show nginx               → details: version, size, dependencies

update vs upgrade — Not the Same Thing

apt update downloads the list of what's available — it changes nothing on your system. apt upgrade actually installs the newer versions. Running upgrade without update first means upgrading against a stale list; running update without upgrade means you know updates exist but haven't applied them. That's why you always see them paired:

sudo apt update && sudo apt upgrade -y

# See what WOULD change before committing
apt list --upgradable

Where Packages Come From

Repository sources live in /etc/apt/sources.list.d/. Third-party software (Docker, Grafana, Tailscale) is added as an extra repo with its own signing key — that's what those curl | gpg lines in install docs are doing. After adding a repo, the package updates through the same apt upgrade as everything else.

# The modern pattern for adding a third-party repo (Docker example)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo "deb [signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu noble stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list

sudo apt update && sudo apt install docker-ce

# What repos am I trusting right now?
grep -rh ^deb /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null

dpkg — The Layer Underneath

apt is the smart front-end; dpkg is the low-level tool that actually unpacks .deb files. You mostly meet it when investigating what's installed or who owns a file:

dpkg -l                     → list every installed package
dpkg -l | grep nginx        → is it installed, and what version?
dpkg -L nginx               → list every file a package installed
dpkg -S /etc/nginx/nginx.conf  → which package owns this file?

# Installing a downloaded .deb properly (apt resolves its dependencies)
sudo apt install ./some-package.deb

Keeping a Lab Updated Safely

For homelab servers, the goal is boring: security patches applied automatically, bigger upgrades applied deliberately.

# Automatic security updates (Debian/Ubuntu)
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

# Hold a package at its current version (e.g. before a risky major bump)
sudo apt-mark hold proxmox-ve
sudo apt-mark unhold proxmox-ve

# What did apt do recently? (great for "what changed before it broke?")
less /var/log/apt/history.log

Rule of thumb: patch weekly, snapshot before major upgrades. If the box is a VM, a pre-upgrade snapshot turns a broken upgrade from an incident into an undo.

≈·*•—[ A|S ]—•*·≈

- Crafted by Axiom|Spectre