Skip to main content

Pinning in Debian for stable servers

·1011 words·5 mins·
Table of Contents

If you are running Debian servers in a “Stable” distribution, you will notice that sometimes certain packages are lagging a bit too much before they enter from Testing into Stable. For instance, this website is made using Hugo, and I like to have a rather recent toolchain, esp. if one or more themes depend on a more recent version of Hugo.

This article will focus on installing specific packages from “Debian Testing” in a “Debian Stable” environment, and keep the default for all other packages to “Debian Stable”.

Assumptions
#

I will assume that you are using the updated .source DEB822-style format for APT. The older format that used .list files will deprecate, probably around year 2029. For more information, read the fine manual:

man sources.list

Overall requirements
#

I want my Debian server to have the following setup when it comes to APT upgrades:

  • By default, all packages must be aligned with Debian/stable’s latest package list.
  • As “suite” I want to run Debian’s latest named “trixie”, which is Debian v13. When Debian v14 is released with codename “forky”, I want to stick with “trixie” until I have the time and energy to back up, upgrade and test the whole distribution to the newer major version. Using “stable” is therefore not recommended, and I will need to resort to fixed codenames like “trixie”, “bookworm” or “forky”.
  • Because of the previous point, I want to make sure that I still get urgent security-related updates for “trixie” for as long as Debian provides long-term support (LTS), or when I upgrade to a newer stable distribution.
  • I want to select some packages from Debian “testing”, and hence allow pinning. I do not want to bother with a particular codename like “trixie” or “forky”; just suite name “testing” will do.
  • I don’t need Debian source files (type deb-src).
  • I want to use a local mirror to speed up retrieval (Hetzner in my case), and have a nearby backup mirror, preferably in the same country as the server. Check out Debian’s official list of mirrors.

Configuration files
#

We modify the default /etc/apt/sources.list.d/debian.sources as follows:

 1# The Debian APT repositories in Finland, pointing statically to "trixie"
 2Types: deb
 3URIs: http://ftp.fi.debian.org/debian/
 4Suites: trixie trixie-updates
 5Components: main contrib non-free-firmware
 6Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
 7
 8# Separate APT repository for security-related updates
 9Types: deb
10URIs: http://security.debian.org/debian-security/
11Suites: trixie-security
12Components: main contrib non-free-firmware
13Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg

My host Hetzner automatically created two files hetzner-mirror.sources and hetzner-security-updates.sources when setting up the initial server so I will stick to their convention, but feel free to combine all stanzas in one file, or even put it under debian.sources. Your choice.

In /etc/apt/sources.list.d/hetzner-mirror.sources we put:

1Types: deb
2URIs: http://mirror.hetzner.com/debian/packages/
3Suites: trixie trixie-updates testing
4Components: main contrib non-free-firmware non-free
5Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg

Note that for Debian/stable we use the codename trixie, and for Debian/testing we simply keep the testing suite as per the requirements above.

And in hetzner-security-updates.sources we put:

1Types: deb
2URIs: http://mirror.hetzner.com/debian/security/
3Suites: stable-security
4Components: main contrib non-free
5Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg

Why not trixie-security? I always want to have the latest stable security updates, even after Debian has released forky as successor of trixie. I do not know when I have the time to upgrade my server environment and test all services, that could take some time.

We create a new custom preference file for APT called /etc/apt/preferences.d/99mycustomprefs and add the following text:

1Package: *
2Pin: release a=testing
3Pin-Priority: -10
4
5Package: hugo
6Pin: release a=testing
7Pin-Priority: 700

This means that we specify that we do not want APT to automatically upgrade any package from Debian/testing, unless the package is exactly named hugo, whose pin priority is set to 700, i.e. a higher priority than the default pin priority of 500 (e.g. given my .sources setup above I have Pin: release a=testing, Pin: release n=trixie-updates, etc.).

Updating the sources
#

The apt command is nowadays the preferred command, instead of the various subcommands like apt-get or apt-cache.

1sudo apt purge
2sudo apt update

If everything went right, we should now notice dozens of lines, each representing the priority of each repository:

Testing
#

apt policy

Let us test two packages: apt as a normal Debian package, and hugo as the package for which we defined a specific pinning policy:

apt policy hugo apt

The output should be something similar as follows:

 1hugo:
 2  Installed: 0.152.1-1
 3  Candidate: 0.152.2-1
 4  Version table:
 5     0.152.2-1 700
 6        -10 http://mirror.hetzner.com/debian/packages testing/main arm64 Packages
 7 *** 0.152.1-1 100
 8        100 /var/lib/dpkg/status
 9     0.131.0-1 500
10        500 http://ftp.fi.debian.org/debian trixie/main arm64 Packages
11        500 http://mirror.hetzner.com/debian/packages trixie/main arm64 Packages
12apt:
13  Installed: 3.0.3
14  Candidate: 3.0.3
15  Version table:
16     3.1.11 -10
17        -10 http://mirror.hetzner.com/debian/packages testing/main arm64 Packages
18 *** 3.0.3 500
19        500 http://ftp.fi.debian.org/debian trixie/main arm64 Packages
20        500 http://mirror.hetzner.com/debian/packages trixie/main arm64 Packages
21        100 /var/lib/dpkg/status

Granted, the output is not too intuitive and poorly undocumented, but what line 5 means is that package hugo version “0.152.2-1” will prioritise the Hetzner/testing repository (700) over any package in trixie from either ftp.fi.debian.org or Hetzner as they only have a priority level of 500. The line with three asterisks is the version that is currently installed on my system. /var/lib/dpkg/status just means whatever you have installed locally.

Conversely, the package apt has a more recent version “3.1.11” on line 16, but it has a negative priority, meaning Debian will never automatically upgrade the package. Unless I specifically install a different version, apt will just stick to whatever version we have in trixie.

Conclusion
#

This is it. The process entails a few more extra steps than what most online guides provide, but they are mostly only suited for development environments or user-centric desktop environments where more recent software is often preferable. In my case, I want a setup that is stable, won’t automatically upgrade to a major new release to avoid nasty compatibility issues between software versions, yet keep up-to-date with the latest security releases.

As always, your requirements may be different. You can read more about APT’s configuration on the official Debian Wiki and implement a configuration that makes sense for you or your organisation.

Danai Sae-Han
Author
Danai Sae-Han
For the past 17 years, Danai has worked for over 50 clients of different sizes, cultures and locations to implement and integrate different software stacks from Ray.io, PyTorch, Grails, OpenText and SAP. You can reach out via the Contact page or through some of the social links.