Automated Updates For Debian-Based Systems
In the video below, we show you how to automatically upgrade Debian based computers
Now, at some point, you’ll come to the conclusion that manually updating a computer is actually quite boring. Or someone will tell you that you need to automate updates—at least for security reasons.
Well, if you feel compelled to automate updates and you don’t want to get up one morning to find your computer has stopped working… you’re in the right place.
In this deep dive, I walk through unattended-upgrades for Debian, Ubuntu, Linux Mint, and other Debian-based systems — explaining when automated updates make sense, when they don’t, and how to configure them safely without nasty surprises.
This is a long, technical, real-world guide covering strategies, common gotchas, and the mistakes that can happen if you “just turn it on and hope for the best.”
Useful links:
https://wiki.debian.org/UnattendedUpgrades
https://wiki.debian.org/DebianReleases
https://wiki.debian.org/DebianReleases/PointReleases
https://wiki.debian.org/StableUpdates
https://wiki.debian.org/StableProposedUpdates
https://www.freedesktop.org/software/systemd/man/latest/systemd.time.html#Calendar%20Events
Installation:
First we’ll update the repository information as we want the latest version of anything we install
sudo apt updateNow we’ll install the unattended-upgrades package
sudo apt install unattended-upgrades -yAs an extra step we’ll run the following command as well
sudo dpkg-reconfigure unattended-upgradesAt the prompt select Yes and hit return
Next, we’ll check one of the config files
cat /etc/apt/apt.conf.d/20auto-upgradesWe want to see the following entries
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";So the goal here is to update the package list and upgrade installed packages on a daily basis
Next we need to edit the default configuration file for unattended-upgrades to decide what automation we want
As an aside, the suggestion is to copy the default configuration file and create a newer one to override it
Personally, I edit the main file because as far as I’m aware you can’t comment something out in the override file; It would be ignored because it’s a comment
I’ll use nano to edit the file
sudo nano /etc/apt/apt.conf.d/50unattended-upgradesAt this stage, what we’re interested in is what software patching we want to be automated
And that’s what the Unattend-Updrade::Origins-Pattern section is about
Now Debian provide separate patching for software bugs, represented by these two lines
// "origin=Debian,codename=${distro_codename}-updates";
"origin=Debian,codename=${distro_codename},label=Debian";And security vulnerabilities, which are covered by these two lines
"origin=Debian,codename=${distro_codename},label=Debian-Security";
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";The way I look at this patching, Debian operate what I’d refer to as a point based system
So as an example, at the initial public release of Trixie, you could download version 13.0 to install on your computer
Now as time goes by, software bugs and security vulnerabilities will be identified and fixed
As a result, newer versions of a file are being released that contain fixes for either, or and even possibly both
After a period of time, Debian then release a new 13.1 version to download and install on new computers which includes all of those patches
And that’s much more preferable than downloading 13.0 and then applying the latest patches
For existing installations, there are choices to be made in terms of what to automatically update
You could for instance choose to automatically apply the regular stable updates to your computer, and that’s what this line is for
"origin=Debian,codename=${distro_codename},label=Debian";By default, the line is active and so when these updates are released, your computer will be patched and brought up to date
So like the new 13.1 release, your computer will then be patched against all of the software bugs and security vulnerabilities that were addressed up to that moment in time
Now some companies and/or administrators, prefer to manually update software during maintenance windows
In that case, they would either remove the line or comment it out like this
// "origin=Debian,codename=${distro_codename},label=Debian";Because, although Debian is focused on stability and reliability, mistakes can and do happen
And although automated updates save people time, manually updating software provides more control should something go wrong
Now between each point release, they are supplying patching considered too important to wait for
And when it comes to security updates, these are extremely important and what automated updates are typically used for
"origin=Debian,codename=${distro_codename},label=Debian-Security";
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";The first of those lines is for backward compatibility with Buster, version 10 and earlier
There’s no harm leaving the line in, but for most computers it won’t do anything
And you check this by running this command
sudo apt-cache policyYou should see lines like these
500 https://security.debian.org/debian-security trixie-security/main amd64 Packages
release v=13,o=Debian,a=stable-security,n=trixie-security,l=Debian-Security,c=main,b=amd64Where o refers to the origin, n to the codename and l for the label as the config file shows
But any case, I just wanted to point out why we see two security lines here instead of one
Now the interim software bug fixes are covered by this line
// "origin=Debian,codename=${distro_codename}-updates";And these are typically covering software bugs that too critical to wait
But it’s still commented out for a reason
Granted you could miss out on critical bug fixes by not enabling this, but enabling it means there will be more updates and so you’re taking on more risk
And do bear in mind, a file could receive updates for software bugs as well as security vulnerabilities during these interim periods
So you might download a security patch that also includes fixes for a recent software bug and vice versa
Now there is another line here covering proposed updates
// "origin=Debian,codename=${distro_codename}-proposed-updates";Most user’s won’t be concerned with these and you would have to configure your source list file to request them
Even though they might include critical fixes, I’d say these are more for testers than general users as they’ve yet to be fully tested
A new entry I’ve only noticed since Trixie came out covers backports
// "o=Debian Backports,n=${distro_codename}-backports,l=Debian Backports";But that’s just because it’s been moved higher up in the examples
Again, it’s commented out and you would have to update your source list to get access to these
From what I understand, this covers newer applications for instance that could become available in the next major release but have been backported to run on the current stable release
Debian releases major versions roughly every two years and so over time the software gets outdated
This provides an opportunity to access those newer versions without having to migrate to a non-stable OS
But this is considered more for testing purposes
Third Party Updates:
Now the config file that was generated is intended to cover software obtained through the official Debian repositories for instance
But if you’ve installed software through 3rd party repositories you might want to consider including those in automated updates
On a Promox VE server for instance, when I ran this command
apt-cache policyOne of the entries I found was this
500 http://download.proxmox.com/debian/pve bookworm/pve-no-subscription amd64 Packages
release o=Proxmox,a=stable,n=bookworm,l=Proxmox Debian Repository,c=pve-no-subscription,b=amd64
origin download.proxmox.comSo PVE could be upgraded as well by adding this line
"origin=Proxmox,codename=${distro_codename},label=Proxmox Debian Repository";That’s because apt-cache policy gave us this information for the origin
o=Proxmox
And this for the label
l=Proxmox Debian Repository
Personally, I wouldn’t be comfortable with automated upgrades like this for a hypervisor, but it’s an example of what’s possible
In my experience, stable software updates have been rolled out, but if something went wrong….
Blacklisting:
If you look further down the file, you’ll come across this section
Unattended-Upgrade::Package-Blacklist {
// The following matches all packages starting with linux-
// "linux-";
...This is where you can define packages that you don’t want to upgrade automatically and several examples are shown
Usually, this is for when you need to retain a specific version for functionality
But, some software isn’t favourable to automatic updates
Wazuh being a good example, because if the server and agent versions are out of synch it doesn’t work
So you have to update the server and all the agents at the same time
But in any case, if you do have software you need to exclude from automated updates, this is where you can do that
Kernel Packages:
Now, most Linux software can be updated on the fly, without needing a computer reboot, although it is still recommended to do that every now and again
But when a new kernel package is released, it won’t be used until the computer is restarted
Now there is the option in the config to automatically reboot the computer
// Automatically reboot *WITHOUT CONFIRMATION* if
// the file /var/run/reboot-required is found after the upgrade
//Unattended-Upgrade::Automatic-Reboot "false";But I prefer to reboot computers manually
That’s because a kernel update could result in the computer not coming back up
The only trouble is, over time, as newer kernel packages are downloaded this will eat into your disk space
And that’s a big problem for virtual machines in particular, as they usually don’t have much storage
Older kernel packages don’t get automatically removed because you may need to fall back to an older one if you run into issues
And while I’ve seen some claims that only a certain number of versions will be kept after an upgrade, I haven’t found that to be the case
TIP: To check which ones are on your computer, and using up disk space, run the following command
dpkg -l | grep linux-image | grep iiAs part of this automation, we can automatically remove unused kernel files
WARNING: Do this at your own risk!
Look for the following line
//Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";If you want to automatically remove older kernels, uncomment that line
But bear in mind, this doesn’t offer a means to keep a certain number of versions
And that can lead to problems, hence why it’s likely commented out
Let’s say a new kernel is downloaded and after a reboot that newer kernel is now being used
The next time the automation runs, it will remove the previous kernel package
Now if something goes wrong, and it causes the computer to keep crashing, you can’t then boot from an older kernel when Grub loads?!?
So for me, manual removal is the better choice
One option would be to run this command
sudo apt autoremove --purgeIt should leave you with the current version, plus one to fallback to
Alternatively you could remove a specific version, for example
sudo apt purge linux-image-6.1.0-40-amd64But you always want at least one version to fallback to, just in case you later run into issues with the current one
Dependencies:
Sometimes after upgrades, there can be packages that are no longer required because they were installed as dependencies
In other words, when a package is installed it may have a dependency on another package, so that dependency is installed as well
Now when a package is upgraded, a dependency may need to be installed, but after the upgrade is completed, that dependency may no longer be needed
So to me, it makes sense to remove these dependencies and not waste disk space and that’s what this line refers to
//Unattended-Upgrade::Remove-New-Unused-Dependencies "true";So uncomment that line if you want dependencies like these to be removed automatically
Now sometimes when a package is upgraded, it may no longer need an original dependency. For instance, the developers might switch to a different software library
And that’s what this line is for
//Unattended-Upgrade::Remove-Unused-Dependencies "false";Now this one can be tricky as it’s possible for a package to be marked for removal by mistake
As the comments show, this is the equivalent of running apt-get autoremove and usually that’s a command you run manually, just in case
But, if you want to automatically remove these types of dependencies, you should also change the setting to true
Unattended-Upgrade::Remove-Unused-Dependencies "true";Dry Run:
Now there are a lot of other settings in that config file and I do suggest you check them out
But what we’ll do next is a dry run to at least check that the config files are OK
sudo unattended-upgrade --dry-runNow in my video example, there was no response, which is actually a good thing, because it hasn’t returned an error
And it’s better that an error shows up now rather than finding out the next day that your automation doesn’t work
But if you want more details, add the -d parameter which is for debugging
sudo unattended-upgrade --dry-run -dNow in my own example, I know there are actually packages that can be updated sudo apt update
But unattended-upgrades is saying there is nothing to update, and that’s because of the settings applied
So I’ll change the configs to include these patches
This is just an example though which applies to Docker on the computer I’m using and which does have updates available
"origin=Docker,archive=${distro_codename},label=Docker CE";Now we’ll do our dry run again
sudo unattended-upgrade --dry-runAnd this time we can see what the upgrade would do
In which case, we now know what to expect when this runs automatically
Service Check:
Now there is service that’s behind all this and it should already be running but it’s best to check
sudo systemctl status unattended-upgradesIf it’s not then you’ll need to enable and start it
sudo systemctl enable --now unattended-upgradesClear Cache:
Now since we’re automating upgrades, one other task we can setup is to regularly clear the repository cache
So we’ll set that up in another file
sudo nano /etc/apt/apt.conf.d/02periodic// Do "apt-get autoclean" every n-days (0=disable)
APT::Periodic::AutocleanInterval "7";Now save and exit
Feel free, to pick a different interval
But this should clear the local repository cache every 7 days which helps to free up disk space
Logging:
At this point, we should now be setup to get daily checks and if necessary package upgrades
But it does help to know what this automation gets up to
And to find out what unattended-upgrades is getting up to, you can check its log file
sudo cat /var/log/unattended-upgrades/unattended-upgrades.logGranted there is a lot of information in here, but it should record software being upgraded, removed and so on
And that’s especially useful if you want to make sure it’s doing it’s job, but also if something stops working and you need to do some troubleshooting
Re-Scheduling:
This upgrade tool is split into two separate tasks; one to update the package list and one to upgrade the installed packages
For the package list, there are two windows of 06:00 and 18:00 but there’s an extra 12 hours added for randomisation
sudo systemctl cat apt-daily.timer# /usr/lib/systemd/system/apt-daily.timer
[Unit]
Description=Daily apt download activities
[Timer]
OnCalendar=*-*-* 6,18:00
RandomizedDelaySec=12h
Persistent=true
[Install]
WantedBy=timers.targetSo it might run early in the morning or it might run late at night, but it should only run once per day
Package upgrades on the other hand will be done between 06:00 and 07:00
sudo systemctl cat apt-daily-upgrade.timer # /usr/lib/systemd/system/apt-daily-upgrade.timer
[Unit]
Description=Daily apt upgrade and clean activities
After=apt-daily.timer
[Timer]
OnCalendar=*-*-* 6:00
RandomizedDelaySec=60m
Persistent=true
[Install]
WantedBy=timers.targetNow I have to factor in other things going on like automated backups overnight that may interrupt package upgrades or vice versa
So what do you do if you want to apply upgrades between say 05:00 and 05:30 instead?
Well although you can edit a main configuration file, it’s better to add an override when you can
That way, even if a software update reverts the main configuration back to the default settings, your override will remain intact
First we’ll re-schedule updating the policy list
sudo mkdir -p /etc/systemd/system/apt-daily.timer.d
sudo nano /etc/systemd/system/apt-daily.timer.d/override.conf[Timer]
OnCalendar=
OnCalendar=04:15
RandomizedDelaySec=15m
Persistent=falseNow save and exit
I need to make sure the package list is updated first, so this in this example it will start somewhere between 04:15 and 04:30
There isn’t much to download, and this is pretty quick, so the timing should be fine
But we have to reset the default timer first, so we begin with OnCalendar=
And I’m using a random delay so not all computers will try to update at the same time
In the default settings, we saw Persistent=true
That means if the task doesn’t run on time, it will be run at the next opportunity
But I’ve set this to false because let’s say there was a power outage, and the task didn’t run. With this setting it will wait for the next day to run again
Why do I prefer this?
Well, I’ve found that if a computer powers up and it needs to run both apt update and apt upgrade it could lock the database
But, it could also start upgrading software during normal hours and that would add further delay to getting services back online
Now since the automation runs every day, I can afford to miss one day
Next we’ll re-schedule the upgrades themselves
sudo mkdir -p /etc/systemd/system/apt-daily-upgrade.timer.d
sudo nano /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf[Timer]
OnCalendar=
OnCalendar=05:00
RandomizedDelaySec=30m
Persistent=falseNow save and exit
This will start between 05:00 and 05:30, again to balance out the downloads
I don’t usually have much software on computers so for me this should all be finished before we reach working hours
For these changes to take effect, we first need to perform a daemon reload
sudo systemctl daemon-reloadThen the services can be restarted
sudo systemctl restart apt-daily.timer
sudo systemctl restart apt-daily-upgrade.timerWe’ll then check to make sure the services are still working
sudo systemctl status apt-daily.timer
sudo systemctl status apt-daily-upgrade.timerTIP: The trigger line will tell you when the service will next run
TIP: If you want to check the settings that are applied you can run these commands
sudo systemctl cat apt-daily.timer
sudo systemctl cat apt-daily-upgrade.timerThis time, you’ll see the default settings followed by any overrides which take precedence
With that done, you should now have updates taking place during more acceptable times
Notifications:
Now although we’re automating upgrades, you’ll still you need to keep an eye on this and unattended-upgrades can send you email notifications
However, for this to work, the computer will need to have a working mail agent but that’s out of scope for this video
But to enable email notifications though we have to edit the config file
sudo nano /etc/apt/apt.conf.d/50unattended-upgradesLower down you’ll see these two lines
//Unattended-Upgrade::Mail "";
//Unattended-Upgrade::MailReport "on-change";As example, let’s say you want a notification when there has been an update applied
In that case, these settings would make sense, although you’ll need to uncomment them and provide the correct email address to send notifications to
Unattended-Upgrade::Mail "your@email.com";
Unattended-Upgrade::MailReport "on-change";You can also set this to only be notified when there has been an error (only-on-error) or you could ask to always be sent a message (always)
But again, the computer needs an agent to be able to send emails, so Postfix for instance
Unfortunately, unattended-upgrades doesn’t offer anything more sophisticated than this
Personally, I only want to know if a reboot is needed or if something has gone wrong
But this has no built-in support for other messaging systems like MQTT
However, you could configure Postfix for instance to intercept a mail notification and send an MQTT message instead
I already do that for VM backups so I’ll do something similar with these updates
But in any case, hopefully you now know how to automate upgrades for Debian based computers
Sharing is caring!