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 ]—•*·≈