Unattended security upgrades, the secure way
The patch for the bug in the news came out nine days ago. Your host still runs the old version, because patching is on the list for "next sprint", and next sprint has been next sprint since spring.
The short answer
On Debian and Ubuntu, install unattended-upgrades, enable it in 20auto-upgrades, and limit it to the security origins with #clear in a local file. On RHEL and Rocky, set upgrade_type = security and apply_updates = yes in dnf-automatic and enable its timer. Plan reboots, and check the dry run output.
On this page
What goes wrong
Most attacks on Linux hosts do not use new bugs. They use old ones with a public fix that nobody installed. The window between "fix released" and "fix installed" is where the damage happens.
Manual patching fails for a simple reason: it depends on someone remembering. Automatic security updates remove that dependency for the updates that matter most.
Two traps make automatic updates look on when they are not. On Debian, the package can be installed but disabled, and a local config file that "sets" the allowed origins only adds to the stock list, because apt merges lists. On both families, a kernel or libc update does nothing until the host reboots.
What the docs say
Most versions of Debian install unattended-upgrades with fairly conservative settings, but your system might not have installed the package at all, or might have installed it but disabled it altogether.
Source: Debian wiki, UnattendedUpgrades
The #clear command is the only way to delete a list or a complete scope.
Source: apt.conf(5), Debian 12
What kind of upgrades to look at. default signals looking for all available updates, security only those with an issued security advisory.
Source: DNF documentation, dnf-automatic, upgrade_type
The Debian wiki tells you to copy 50unattended-upgrades to a local file and
edit the origins. It does not warn that the list in your file is merged with
the stock list, not used instead of it. Without #clear, the stock
label=Debian origin stays allowed.
The secure configuration
Debian and Ubuntu:
apt-get install -y unattended-upgrades apt-listchanges// /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";// /etc/apt/apt.conf.d/52unattended-upgrades-local
// Security updates only. Lists in apt.conf files are MERGED, so first clear the
// list that the stock 50unattended-upgrades built (it also allows "label=Debian").
#clear Unattended-Upgrade::Origins-Pattern;
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename},label=Debian-Security";
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
};
// Never let a half-configured dpkg block the next run.
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
// Install in small steps, so a shutdown mid-run leaves a consistent system.
Unattended-Upgrade::MinimalSteps "true";
// Remove dependencies that upgrades made unused (old kernels included).
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// Reboot when a kernel or libc update needs it, at a quiet hour.
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
Unattended-Upgrade::Automatic-Reboot-Time "03:30";
// Tell someone. Needs a working local MTA.
Unattended-Upgrade::Mail "root";
Unattended-Upgrade::MailReport "on-change";On Ubuntu, the security origin is written "${distro_id}:${distro_codename}-security";
in Allowed-Origins; the #clear rule is the same.
If automatic reboots are not acceptable, set Automatic-Reboot "false" and
alert on the file /var/run/reboot-required instead.
RHEL, Rocky, AlmaLinux and Fedora:
dnf install -y dnf-automatic# /etc/dnf/automatic.conf (the [commands] section)
[commands]
# Only updates that have a security advisory.
upgrade_type = security
download_updates = yes
# Install them, do not just download.
apply_updates = yes
# Reboot only when the kernel, systemd or similar changed.
reboot = when-neededsystemctl enable --now dnf-automatic.timer
systemctl list-timers dnf-automatic.timerProve it
The stock Debian list allows label=Debian (all stable updates, not only
security):
apt-config dump Unattended-Upgrade::Origins-PatternUnattended-Upgrade::Origins-Pattern "";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename},label=Debian";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename},label=Debian-Security";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";The trap: a local file with only the security origin, and no #clear. The
stock entries are still there:
Unattended-Upgrade::Origins-Pattern "";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename},label=Debian";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename},label=Debian-Security";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";With the full local file above (#clear first), the list holds only the
security origins, and the schedule is on:
apt-config dump APT::Periodic
apt-config dump Unattended-Upgrade::Origins-PatternAPT::Periodic "";
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
Unattended-Upgrade::Origins-Pattern "";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename},label=Debian-Security";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";A dry run shows what the next run would install:
unattended-upgrade --dry-run --debugStarting unattended upgrades script
Allowed origins are: origin=Debian,codename=bookworm,label=Debian-Security, origin=Debian,codename=bookworm-security,label=Debian-Security
Initial blacklist:
Initial whitelist (not strict):
Checking: tzdata ([<Origin component:'main' archive:'oldstable-security' origin:'Debian' label:'Debian-Security' site:'deb.debian.org' isTrusted:True>])
pkgs that look like they should be upgraded: tzdata
Option --dry-run given, *not* performing real actions
Packages that will be upgraded: tzdataRocky Linux 9, after editing automatic.conf:
grep -E "^(upgrade_type|download_updates|apply_updates|reboot|emit_via)" /etc/dnf/automatic.conf
dnf -q updateinfo list --security | head -5
dnf -q updateinfo list --security | wc -lupgrade_type = security
download_updates = yes
apply_updates = yes
reboot = when-needed
reboot_command = "shutdown -r +5 'Rebooting after applying package updates'"
emit_via = stdio
RLSA-2026:60226 Moderate/Sec. attr-2.6.0-1.el9_8.x86_64
RLSA-2025:0925 Moderate/Sec. bzip2-libs-1.0.8-10.el9_5.x86_64
RLSA-2026:66403 Moderate/Sec. coreutils-single-8.32-41.el9_8.1.x86_64
RLSA-2026:28911 Moderate/Sec. coreutils-single-8.32-41.el9_8.x86_64
RLSA-2026:1350 Moderate/Sec. curl-7.76.1-35.el9_7.3.x86_64
123The container image is a few months old, so it has 123 advisory lines
waiting; those are exactly what upgrade_type = security would install. The
test script is secure-tests/unattended-security-upgrades/run.sh.
Mistakes people make
Installed but never enabled
unattended-upgrades does nothing without APT::Periodic::Unattended-Upgrade "1".
Check with apt-config dump APT::Periodic, and look for a recent run in
/var/log/unattended-upgrades/unattended-upgrades.log.
Editing the origins without #clear
Your list is added to the stock list. apt-config dump shows the merged
result; trust it, not the file you edited.
Patching without rebooting
A new kernel or libc on disk does not protect a process that still runs the
old one. Enable automatic reboots in a window, or alert on
/var/run/reboot-required (Debian) and dnf needs-restarting -r (RHEL family).
Nobody reads the result
A failed run looks exactly like a quiet night. Send the report somewhere a person reads it, or alert when the last successful run is older than two days.
Third-party repositories in the allowed list
Adding a vendor origin to the automatic list lets that vendor push code to your hosts at 3 a.m. Keep vendor repositories out, or pin them first.
Checklist
unattended-upgradesordnf-automaticis installed.apt-config dump APT::PeriodicshowsUnattended-Upgrade "1", ordnf-automatic.timeris enabled.apt-config dump Unattended-Upgrade::Origins-Patternshows only the origins you chose.upgrade_type = securityandapply_updates = yesare set on RHEL-family hosts.- A dry run lists the expected security packages.
- Reboots happen automatically in a window, or an alert fires when one is needed.
- Run reports reach a person or an alerting system.
- The last successful run is less than two days old.
Automatic patching will occasionally annoy you. Not patching will eventually embarrass you. Pick the annoyance.
FND
Learn it on a live range
Linux 3: securing Linux, in Foundation: a real host in your browser, and every objective checked on the machine.
Start freeThe Secure Way
More on linux hosts
SSH, sudo, firewalls, mount options, updates and the host basics every engineer should get right.
All linux hosts guides