28.5 Pinning and Holding: Preventing Specific Package Upgrades
Right, so you’ve decided to get fancy. Or more likely, you’ve been burned. Something tried to upgrade and it broke your perfectly curated setup. Maybe it was a new version of nginx that changed a critical config file syntax, or a dependency for your custom-built application got a backwards-incompatible update. Welcome to the big leagues. This is where we move from just letting apt do whatever it wants to telling it exactly what we expect of it. We’re going to talk about pinning and holding, the two primary methods for preventing specific packages from upgrading.
The Simple, Blunt Instrument: apt-mark hold
Let’s start with the easy one. When you just need to absolutely, positively stop a single package from being upgraded, ever, until you say so, you use hold. It’s the digital equivalent of putting a brick on the package’s head.
Think of it like this: apt-get upgrade is a bouncer checking a list. A package on hold is on that list with a big, red “DO NOT TOUCH” stamp next to it. The bouncer just skips it. It’s incredibly simple and effective.
You hold a package with:
sudo apt-mark hold package_name
And when you’ve come to your senses (or finally tested the new version), you unhold it with:
sudo apt-mark unhold package_name
To see your list of held packages, which is a fantastic idea for sanity checks:
apt-mark showhold
Why would you use this? It’s perfect for emergency stops. That docker-ce version that just dropped and is known to be borked? Hold it. The libc6 upgrade that you know will break three legacy apps you still need to run? Hold it. It’s your first line of defense. The pitfall? It’s all or nothing. The package is frozen in time, vulnerabilities and all, until you manually intervene. This is a tactical tool, not a strategic one.
The Precise, Surgical Tool: Pinning with apt-preferences
Now, hold is great, but it’s dumb. What if you don’t want to block a package forever, you just want to prioritize one repository over another? This is the classic use case for pinning. Say you’re running a mostly stable Debian bullseye system, but you need a newer version of ffmpeg that’s only in the bullseye-backports repo. You want to pull ffmpeg from backports, but you want everything else to stay firmly on the standard bullseye releases. This is where the /etc/apt/preferences file (and files in /etc/apt/preferences.d/) comes in.
Pinning works by assigning a priority (a number from 0 to 1001) to packages from different origins. The rules are simple: APT will always install the version with the highest priority. If priorities are equal, it installs the newest version. Here’s the cheat sheet:
- 1001:
requiredpriority (core system stuff, you can’t override this even if you tried). - 100 to 999:
preferredpriorities. This is your working range. - 500: The default priority for all packages.
- 100: The priority of the
stablerelease. - 1 to 99:
non-preferredpriorities. You can use this to downgrade the importance of a repo. - <0: Effectively disables the package from that origin.
So, for our ffmpeg from backports example, we need to give packages from the backports origin a higher priority than 500, but only for the specific packages we want. We don’t want to wholesale upgrade our system from backports; that way lies madness.
Here’s how you’d craft a file, say /etc/apt/preferences.d/90_backports-ffmpeg:
Package: ffmpeg
Pin: release a=bullseye-backports
Pin-Priority: 600
Package: *
Pin: release a=bullseye-backports
Pin-Priority: 100
Let’s break down this masterpiece of passive aggression:
- The first stanza: “For the package
ffmpeg, if it comes from the release where the suite (a=) isbullseye-backports, give it a nice high priority of 600.” This means APT will eagerly upgradeffmpegfrombackports. - The second stanza: “For EVERY other package (
*) frombackports, give it a lowly priority of 100.” Since the default stable repo is at 500, this makes APT actively avoid installing anything else frombackportsunless it’s explicitly requested or there’s no other version. It brilliantly isolates the chaos.
You can see the brilliant, terrifying results of your pinning with:
apt-cache policy ffmpeg
This will show you all available versions and their assigned priorities. Your chosen version should have a priority of 600, making it the winner.
The designers made this syntax needlessly cryptic (a= for suite? n= for name? It’s an arcane incantation). The common pitfall is being too broad with your pin and accidentally pulling in half of Debian testing because you gave it a priority of 999. Always check your work with apt-cache policy.
So, to recap: use hold for a quick, total freeze. Use pinning when you need nuanced, repository-based control. Both are essential skills for keeping a system that’s both stable and useful, which is, after all, the whole point.