Configuring Unattended Upgrades on Debian

In this post, I show you how to configure the Unattended Upgrades service on Debian-based distributions.
Configuring Unattended Upgrades on Debian
In: Home Lab, Defend, Linux

Installation

sudo apt install -y unattended-upgrades apt-listchanges

With the installation complete, we're primarily concerned with the configuration of two files:

  • /etc/apt/apt.conf.d/50unattended-upgrades
  • /etc/apt/apt.conf.d/20auto-upgrades

When you manually install upgrades with apt, you'd probably run something like, apt update && apt upgrade -y --autoremove && apt autoclean. All you're really doing is automating this process with the use of unattended-upgrades.



Choosing What to Upgrade

Origins Pattern

Origins Pattern controls which apt sources will be read by unattended-upgrades to pull and upgrade packages automatically.

tail -n 146 /etc/apt/apt.conf.d/50unattended-upgrades | head -n 13
Unattended-Upgrade::Origins-Pattern {
        // Codename based matching:
        // This will follow the migration of a release through different
        // archives (e.g. from testing to stable and later oldstable).
        // Software will be the latest available for the named release,
        // but the Debian release itself will not be automatically upgraded.

        // Archive or Suite based matching:
        // Note that this will silently match a different release after
        // migration to the specified archive (e.g. testing becomes the
        // new stable).
        origin=${distro_id},codename=${distro_codename};
};

You'd probably see something like this in a default installation of Unattended Upgrades

Note that every line starting with // is a comment and is not processed by Unattended Upgrades. Read them to understand more about each section. The interesting line is:

origin=${distro_id},codename=${distro_codename};



Making Sense of Origins

What are the two variable names we see here?

  • ${distro_id}
  • ${distro_codename}
/etc/apt/apt.conf.d/50unattended-upgrades

You can translate these variable names by the value(s) in /etc/debian_version, but can also be found in /etc/os-release or by running lsb_release -a.

Output of lsb_release -a command run on a Kali VM
  • ${distro_id}
    • lsb_release -aDistributor ID = Kali
  • ${distro_codename}
    • lsb_release -aCodename = kali-rolling

Therefore, unattended-upgrades will translate the origins pattern to:

"origin=Kali,codename=kali-rolling";



How It Fits Together

We now know that when unattended-upgrades is run, it will look at the origins pattern block and translate "origin=${distro_id},codename=${distro_codename}" to those from lsb_release -a.

/etc/apt/apt.conf.d/50unattended-upgrades
A package will be upgraded only if the values in its metadata match all the supplied keywords in a line. (In other words, omitted keywords are wild cards.)

A valid release pattern can contain the keywords:

  • a, archive, or suite
  • c or component
  • l or label
  • o or origin
  • n or codename
  • and a site hostname
⚠️
We do not have to provide all of the keywords. But, if we omit any keywords, unattended-upgrades treats this as a wildcard and matches loosely based on what you've provided.

The available values on the system are printed by the command "apt-cache policy"
apt-cache policy

Running this command provides any apt sources that will be read by unattended-upgrades

apt-cache policy output on Kali VM
💡
unattended-upgrades does not accept the b= (branch) of the specific source. apt will install the correct packages based on the host's CPU architecture.
Filtering apt releases using the same logic as unattended-upgrade

We can conclude from the information above:

  • Given the release identifier "origin=Kali,codename=kali-rolling";
    • The o= field is translated from ${distro_id} to Kali
    • The a= field is a wildcard, since it was not provided in the origin
    • The n= field is translated from ${distro_codename} to kali-rolling
    • The c= field is a wildcard, since it was not provided in the origin
  • Any package identified in the release identifiers in pink above will be upgraded by unattended-upgrades



A Word of Caution

🚨
unattended-upgrades could unexpectedly upgrade your OS if your archive, a, suite, c, or codename are set to something like stable.

If your /etc/apt/sources are set to something like stable instead of hard-coded Debian versions such as bullseye, bookworm, trixie, etc, then this would cause unattended-upgrades to pull the latest Debian system files in the stable branch using apt and upgrade the entire operating system.



Holding Packages from Upgrades

If there are packages that you wish to be held from automatic upgrades, you can use apt-mark hold or pinning.

sudo apt-mark hold PACKAGE_NAME

Marking a package as "held" to prevent upgrades



Ubuntu-Specific Notes

I'm adding this here just to have it documented, but when I was configuring unattended-upgrades on Ubuntu, I noted that the --dry-run -d parser would only accept configurations with explicitly the o and the a values in an origin pattern. Any other keywords seemed to cause errors with the parser.

// apt-cache policy for MongoDB
// o=mongodb,a=xenial
"mongodb:xenial";

// apt-cache policy for Ubiquiti
// o=Ubiquiti Networks, Inc.,a=stable
"Ubiquiti Networks, Inc.:stable";

In previous testing, Ubuntu requires "colon-delimited" fields, not commas



Configuring the Service

sudo nano /etc/apt/apt.conf.d/50unattended-upgrades

Example 1: Visual Studio Code

Let's use this output from apt-cache policy as an example:

 500 http://packages.microsoft.com/repos/code stable/main armhf Packages
     release o=code stable,a=stable,n=stable,l=code stable,c=main,b=armhf
     origin packages.microsoft.com
 500 http://packages.microsoft.com/repos/code stable/main arm64 Packages
     release o=code stable,a=stable,n=stable,l=code stable,c=main,b=arm64
     origin packages.microsoft.com
 500 http://packages.microsoft.com/repos/code stable/main amd64 Packages
     release o=code stable,a=stable,n=stable,l=code stable,c=main,b=amd64
     origin packages.microsoft.com

These are the branches of the Visual Studio Code apt repositories on my Kali Linux box.

  • I do not need to configure three separate origin patterns for these
  • These sources are all the same with the exception of the branch denoting the CPU architecture
    • b=armhf
    • b=arm64
    • b=amd64
  • On my Kali VM, specifically, apt will automatically use the amd64 branch based on the VM's CPU type.

So, if I wanted to identify a release pattern that will automatically upgrade Visual Studio Code to the latest version, I can copy and paste this single string:

"o=code stable,a=stable,n=stable,l=code stable,c=main";
Unattended-Upgrade::Origins-Pattern {
        // Comments and other content
        // Removed for clarity
        "origin=${distro_id},codename=${distro_codename}";
        "o=code stable,a=stable,n=stable,l=code stable,c=main";
};

Showing origin for VS Code added to the Origins Pattern block



Example 2: Microsoft Edge and Brave Browser

Again, looking at the output from apt-cache policy:

 500 https://packages.microsoft.com/repos/edge stable/main amd64 Packages
     release o=edge stable,a=stable,n=stable,l=edge stable,c=main,b=amd64
     origin packages.microsoft.com
 500 https://brave-browser-apt-release.s3.brave.com stable/main amd64 Packages
     release o=Brave Software,a=stable,n=stable,l=Brave Browser,c=main,b=amd64
     origin brave-browser-apt-release.s3.brave.com

To keep my Microsoft Edge and Brave Browser automatically upgraded, I could copy and paste these strings into the config file:

"o=edge stable,a=stable,n=stable,l=edge stable,c=main";

Microsoft Edge release string

"o=Brave Software,a=stable,n=stable,l=Brave Browser,c=main";

Brave browser release string

Unattended-Upgrade::Origins-Pattern {
        // Comments and other content
        // Removed for clarity
        "origin=${distro_id},codename=${distro_codename}";
        "o=code stable,a=stable,n=stable,l=code stable,c=main";
        "o=edge stable,a=stable,n=stable,l=edge stable,c=main";
        "o=Brave Software,a=stable,n=stable,l=Brave Browser,c=main";
};

VS Code, Microsoft Edge, and Brave Browser in the Origins Pattern block



Configuring Upgrade Options

⚠️
Still editing the /etc/apt/apt.conf.d/50unattended-upgrades file

Now that the origin patterns have been added, scroll down the configuration file and you'll see additional options — some commented out with a // prefix.

Just a few of the options prefixed by // that you may want to configure
💡
You can copy and paste options in a single block for better visibility as shown just below.
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::InstallOnShutdown "false";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
Unattended-Upgrade::OnlyOnACPower "false";

All of the options I've from which I've removed the preceding // and configured

A few more things to note:

  • I auto-reboot at 04:00, change this to a time that suits your needs
  • I haven't shown you how to configure //Unattended-Upgrade::Mail "";, but I'd encourage you to look into this if you're running a production server and/or want to track changes.
  • Read the // Comments above each section to gain a better understanding about each option.



Vendor-Specific Upgrades

🚨
Some distributions, such as Kali Linux, recommend users to run sudo apt full-upgrade, which requires an additional configuration added to unattended-upgrades.

Always consult your distribution vendor's documentation for best practices when performing upgrades.
Unattended-Upgrade::Upgrade-Type "full-upgrade";

Adding the full-uprade option to my Kali Linux VM to follow vendor-specific advice



Configuring the Schedule

sudo nano /etc/apt/apt.conf.d/20auto-upgrades
// How often (in days) to apt update
APT::Periodic::Update-Package-Lists "7";

// How often (in days) to download new packages
APT::Periodic::Download-Upgradeable-Packages "7";

// How often (in days) to clean the apt clean
APT::Periodic::AutocleanInterval "7";

// How often (in days) to run unattended-upgrades
APT::Periodic::Unattended-Upgrade "7";

Weekly schedule



Enable and Start the Service

sudo systemctl enable --now unattended-upgrades

We want the daemon to start at boot and also start immediately



Troubleshooting

Dry Run Mode

sudo unattended-upgrades -d --dry-run

Run unattended-upgrades with output in dry run mode

You can use dry run mode to see if there are any problems with your origin patterns, or problems with your overall configuration. Dry run mode will operate like a normal run, but will not install upgrades.



Manual Run

sudo unattended-upgrades -d

Run unattended-upgrades manually with debug output

Running unattended-upgrades in debug mode will run an actual upgrade of your packages based on your origin patterns. Running it in debug mode manually can be useful when you want to gauge how the program will behave in future unattended runs.



References

UnattendedUpgrades - Debian Wiki
Comments
More from 0xBEN
Infrastructure-as-Code with Proxmox
Proxmox

Infrastructure-as-Code with Proxmox

In this project, broken up into multiple modules, you will gain hands-on, interactive practice with defining and managing Infrastructure-as-Code using industry-standard DevSecOps tooling and zero-trust security principles.
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.