Automated Updates For Debian-Based Systems

Jan 15, 2026 · 18 mins read
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 update

Now we’ll install the unattended-upgrades package

sudo apt install unattended-upgrades -y

As an extra step we’ll run the following command as well

sudo dpkg-reconfigure unattended-upgrades

At the prompt select Yes and hit return

Next, we’ll check one of the config files

cat /etc/apt/apt.conf.d/20auto-upgrades

We 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-upgrades

At 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 policy

You 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=amd64

Where 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 policy

One 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.com

So 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 ii

As 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 --purge

It 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-amd64

But 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-run

Now 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 -d

Now 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-run

And 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-upgrades

If it’s not then you’ll need to enable and start it

sudo systemctl enable --now unattended-upgrades

Clear 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.log

Granted 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.target

So 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.target

Now 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=false

Now 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=false

Now 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-reload

Then the services can be restarted

sudo systemctl restart apt-daily.timer
sudo systemctl restart apt-daily-upgrade.timer

We’ll then check to make sure the services are still working

sudo systemctl status apt-daily.timer
sudo systemctl status apt-daily-upgrade.timer

TIP: 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.timer

This 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-upgrades

Lower 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!