1. Overview

Getting a list of security updates instead of just running apt upgrade or just installing them through the graphical update manager can be helpful in several circumstances:

  • Selective updating → Sometimes we don’t want to apply all updates immediately to avoid potential compatibility issues with critical applications or services
  • Minimizing downtime → By first reviewing a list of security updates, we can strategically schedule updates to minimize the impact on the availability of our production systems
  • Testing → Before deploying updates to all of our production systems, we may want to apply them to a test system first

In this tutorial, we’ll look at several ways to get the list of available security updates on Debian and Ubuntu. For reference, the upcoming examples will refer to fresh installations of Debian 12.2 and Ubuntu 22.04.3 LTS. We’ll assume we’re logged in as root.

2. Disable Automatic Updates

In Debian 12 and Ubuntu 22.04, the enabled repositories and their automatic updates have different defaults due to their different package management policies. These policies may change from one version of each operating system to another.

To check security updates before installing them, it’s advisable to make sure their automatic installation is disabled.

2.1. Terminal Approach

In Debian and Ubuntu, automatic updates are typically managed by the unattended-upgrades service. Let’s install it, even if we don’t want automatic updates, because we’ll use it later to get the list of security updates:

# apt install unattended-upgrades

Then, let’s edit its configuration file with nano /etc/apt/apt.conf.d/20auto-upgrades:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "0";
APT::Periodic::AutocleanInterval "0";
APT::Periodic::Unattended-Upgrade "0";

In this case, “1” enables the option it refers to, and “0” disables it. So, let’s see what these settings mean:

  • Our system checks for updates regularly because we have Update-Package-Lists enabled
  • It won’t automatically download these updates or clean up old packages because Download-Upgradeable-Packages and AutocleanInterval are disabled
  • It won’t automatically install updates because we’ve disabled Unattended-Upgrade

This way, we can manually decide which updates to install.

2.2. GUI Approach

The Software & Updates application that comes by default with both operating systems allows us to enable or disable the available repositories and, in the case of unattended-upgrades installed, to manage automatic updates. We can disable them with the option we’ve highlighted here:

Ubuntu 22.04 updatesIf the Updates tab isn’t present, it’s because we haven’t installed unattended-upgrades.

This screenshot is from Ubuntu. In Debian, the Software & Updates tool is slightly different, but the option we need to set is the same.

3. apt & apt-get vs. unattended-upgrade

We can use both apt or apt-get and unattended-upgrade to list security updates on a Debian or Ubuntu-based system, but they approach the task differently.

3.1. apt & apt-get

First, let’s ask apt to update the package indexes to ensure we have the latest information about available packages:

# apt update
[...]
111 packages can be upgraded. Run 'apt list --upgradable' to see them.
[...]

Next, let’s list all upgradable packages, including security updates:

# apt list --upgradable
Listing... Done
alsa-ucm-conf/jammy-updates,jammy-updates 1.2.6.3-1ubuntu1.8 all [upgradable from: 1.2.6.3-1ubuntu1.7]
amd64-microcode/jammy-updates,jammy-security 3.20191218.1ubuntu2.2 amd64 [upgradable from: 3.20191218.1ubuntu2.1]
apt-utils/jammy-updates 2.4.10 amd64 [upgradable from: 2.4.9]
[...]

In this case, jammy-security refers to the repository that provides security updates for the Ubuntu distribution version known as jammy. More generally, in the context of Debian-based distributions, if a repository contains the string -security, it usually means that it provides security updates.

So, we can use grep to filter out the security updates, but let’s pay attention to the warning that apt generates:

# apt list --upgradable | grep -i "-security"

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

amd64-microcode/jammy-updates,jammy-security 3.20191218.1ubuntu2.2 amd64 [upgradable from: 3.20191218.1ubuntu2.1]
cups-bsd/jammy-updates,jammy-security 2.4.1op1-1ubuntu4.7 amd64 [upgradable from: 2.4.1op1-1ubuntu4.4]
cups-client/jammy-updates,jammy-security 2.4.1op1-1ubuntu4.7 amd64 [upgradable from: 2.4.1op1-1ubuntu4.4]
[...]

The warning “apt does not have a stable CLI interface” doesn’t explicitly tell us to use apt-get instead of apt, but apt-get is indeed more stable for scripting purposes:

# apt-get --just-print upgrade | grep "^Inst.*-security.*"
Inst libc6-dbg [2.35-0ubuntu3.1] (2.35-0ubuntu3.4 Ubuntu:22.04/jammy-updates, Ubuntu:22.04/jammy-security [amd64]) []
Inst libc6 [2.35-0ubuntu3.1] (2.35-0ubuntu3.4 Ubuntu:22.04/jammy-updates, Ubuntu:22.04/jammy-security [amd64])
Inst libc-bin [2.35-0ubuntu3.1] (2.35-0ubuntu3.4 Ubuntu:22.04/jammy-updates, Ubuntu:22.04/jammy-security [amd64])
[...]

We’re almost done. To produce a clean list of security packages without too much information, we need to further filter the previous output using cut:

# apt-get --just-print upgrade | grep "^Inst.*-security.*" | cut -d ' ' -f 2
libc6-dbg
libc6
libc-bin
cups-ipp-utils
cups-common
[...]

To make it easier to consult the list, we can add sort to order it alphabetically:

# apt-get --just-print upgrade | grep "^Inst.*-security.*" | cut -d ' ' -f 2 | sort
amd64-microcode
cups
cups-bsd
cups-client
cups-common
cups-core-drivers
[...]

One advantage of this approach is that it’s very fast since apt-get generates the list of security updates in seconds.

3.2. unattended-upgrades

If we’ve already installed and configured unattended-upgrades, as shown above, we’re ready to use it to get a complete list of security updates. However, we must be aware that unattended-upgrades is slower than apt-get, as it can take a minute or more to provide us with the list of updates.

To simulate security upgrades without actually changing anything, we can use the –dry-run option. Also, we need to use debug mode with the -d flag. Otherwise, we won’t get the list of updates:

# unattended-upgrade --dry-run -d

Unfortunately, its output is extremely difficult to read:

  • In the case of Debian, we get an output 950 lines long
  • In the case of Ubuntu, the output even reaches 2746 lines
  • On both Debian and Ubuntu, the output mixes stdout and stderr messages

We can see, however, that within this extremely verbose output is the information we need:

[...]
pkgs that look like they should be upgraded: amd64-microcode
bind9-dnsutils
bind9-host
[...]
xserver-xorg-core
xserver-xorg-legacy
xwayland
xxd

Fetched 0 B in 0s (0 B/s)
[...]

As a consequence, all we need to do is filter the output of unattended-upgrade –dry-run -d, leaving only the list of packages:

# unattended-upgrade --dry-run -d 2>&1 | \
sed -n '/pkgs that look like they should be upgraded/,/Fetched/p' | \
sed 's/pkgs that look like they should be upgraded: //' | \
grep -v '^[[:space:]]*$' | \
head -n -1

This gives us the list of security updates in alphabetical order:

curl
exim4-base
exim4-config
exim4-daemon-light
firefox-esr
[...]

Here’s a breakdown of how we did the output filtering:

  • 2>&1 → Redirects the standard error (stderr) to standard output (stdout) to process both regular messages and error messages together in the subsequent steps
  • first sed → Prints lines starting from the one containing “pkgs that look like they should be upgraded” to the one containing “Fetched
  • second sed → Removes the initial phrase “pkgs that look like they should be upgraded: ” from the line where it appears, leaving only the name of the first package
  • grep -v ‘^[[:space:]]*$’ → Removes lines consisting entirely of whitespace characters or that are empty
  • head -n -1 → Removes the final line that contains “Fetched

All of this assumes that the format of the unattended-upgrade‘s output is consistent with these versions:

# apt-cache policy unattended-upgrades
[...]
  Installed: 2.9.1+nmu3 # Debian
  Installed: 2.8ubuntu1 # Ubuntu
[...]

If the format of unattended-upgrade‘s output changes in future releases, we may need to modify our command accordingly.

3.3. Comparison of apt-get and unattended-upgrade

While unattended-upgrade is slower, more complex, and less intuitive than apt-get, mainly because of the additional text processing commands it requires, it’s potentially more accurate in identifying relevant packages due to its focus on security upgrades. Let’s do a test on Ubuntu, using wc -l to count the number of updates:

# apt-get --just-print upgrade | grep "^Inst.*-security.*" | cut -d ' ' -f 2 | sort > updates-1.txt
# wc -l updates-1.txt
65 updates-1.txt

# unattended-upgrade --dry-run -d 2>&1 | \
sed -n '/pkgs that look like they should be upgraded/,/Fetched/p' | \
sed 's/pkgs that look like they should be upgraded: //' | \
grep -v '^[[:space:]]*$' | \
head -n -1 > updates-2.txt
# wc -l updates-2.txt
68 updates-2.txt

In this case, unattended-upgrade identified a few more packages. Let’s see which ones using comm:

# comm -3 <(sort updates-1.txt) <(sort updates-2.txt)
	bind9-dnsutils
	bind9-host
	bind9-libs

We only notice this difference on Ubuntu. On Debian, apt-get and unattended-upgrade seem to return identical lists.

4. Conclusion

In this article, we’ve seen how to get the list of security updates available on Debian and Ubuntu.

We used apt-get and unattended-upgrade, which sometimes give identical results, and sometimes unattended-upgrade is more accurate. However, the latter is slower and more complex to use. The former is much faster and more scriptable.

However, both are good solutions.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.