Where to start First steps track · 15 stages
Linux Labs

unattended-upgrades: security updates with no one at the keyboard

12 min Ubuntu 24.04 · unattended-upgrades 2.9 · needrestart 3.6

Ubuntu's service that installs updates from allowed origins once a day on an apt timer; by default only security ones.

Cheat sheet

7 commands Printable version
  • systemctl list-timers 'apt-daily*' when the next run is
  • cat /etc/apt/apt.conf.d/20auto-upgrades is it switched on
  • apt-config dump | grep -E '^APT::Periodic|^Unattended-Upgrade::'
  • sudo unattended-upgrade --dry-run -v what it would install
  • sudo ls /var/log/unattended-upgrades/ run logs
  • sudo needrestart -b who needs a restart
  • ls /var/run/reboot-required is a reboot needed

Overview

unattended-upgrades installs updates with no one at the keyboard. apt timers start it:

Step Timer → service What it does
1 apt-daily.timer → apt-daily.service refreshes package lists once a day, like apt update
2 apt-daily-upgrade.timer → apt-daily-upgrade.service runs unattended-upgrade at 6:00 plus a random delay of up to 60 minutes
3 the DPkg::Post-Invoke hook in 99needrestart after installation, restarts services that run old files
text
student@ubuntu:~$ systemctl cat apt-daily-upgrade.timer \
> | grep -E 'OnCalendar|Randomized|Persistent'
OnCalendar=*-*-* 6:00
RandomizedDelaySec=60m
Persistent=true

Persistent=true means that if the machine was off at 6:00, the run happens after boot. The unattended-upgrades.service unit ("Unattended Upgrades Shutdown") does not install anything: it waits for shutdown so as not to cut an installation short.

Steps 1 and 2 are switched on by 20auto-upgrades; the number is the interval in days:

text
student@ubuntu:~$ cat /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

On the lab machine (unattended-upgrades 2.9.1) 20auto-upgrades turns on both steps, but the timers and the service are off (disabled), and there is no network. The old package lists hold about a hundred and fifty updates, over a hundred of them from noble-security, and a trial run fails at the download:

text
student@ubuntu:~$ sudo unattended-upgrade --dry-run -v
Could not figure out development release: Distribution data outdated.
  Please check for an update for distro-info-data. See
  /usr/share/doc/distro-info-data/README.Debian for details.
Starting unattended upgrades script
Allowed origins are: o=Ubuntu,a=noble, o=Ubuntu,a=noble-security,
  o=UbuntuESMApps,a=noble-apps-security,
  o=UbuntuESM,a=noble-infra-security
Initial blacklist:
Initial whitelist (not strict):
The URI http://security.ubuntu.com/ubuntu/pool/main/u/util-linux/
  bsdutils_2.39.3-9ubuntu6.6_amd64.deb failed to download, aborting

The first line is about a development release and has nothing to do with an LTS server. The trial run takes about a minute: the program goes through every candidate.

What gets installed

The origins are the Allowed-Origins list in 50unattended-upgrades. A package is installed if its origin (o= — who publishes it, a= — the archive suite) matches one of the lines:

text
Unattended-Upgrade::Allowed-Origins {
	"${distro_id}:${distro_codename}";
	"${distro_id}:${distro_codename}-security";
	"${distro_id}ESMApps:${distro_codename}-apps-security";
	"${distro_id}ESM:${distro_codename}-infra-security";
};

${distro_id}:${distro_codename} is the noble release itself, which does not change after release. The -updates line in the file is commented out, so by default only security updates arrive. The ESM suites work with an Ubuntu Pro subscription attached.

Restart after an update

An update changes files on disk. A running program keeps executing the old ones: its file is deleted but still open — an open unlinked file. Until the service is restarted, the fix does not apply to it. The demo-sleep binary was replaced, and the demo-old service keeps running:

text
student@ubuntu:~$ sudo lsof +L1
COMMAND    PID USER  FD   TYPE DEVICE SIZE/OFF NLINK NODE NAME
demo-slee 4214 root txt    REG  253,4    35336     0 4464
  /usr/local/bin/demo-sleep (deleted)
student@ubuntu:~$ sudo readlink /proc/4214/exe
/usr/local/bin/demo-sleep (deleted)

An old shared library stays in memory the same way — marked (deleted) in /proc/<PID>/maps (see proc-filesystem). After systemctl restart demo-old the process starts from the new file, and the mark is gone.

Restarting services does not update the kernel. A package that needs a reboot creates /var/run/reboot-required; the machine reboots by itself only if Automatic-Reboot is on.

needrestart

needrestart finds processes running old files and names their services. -r sets the mode: l only lists, a restarts, i asks. -b prints lines for scripts:

text
student@ubuntu:~$ sudo needrestart -b
NEEDRESTART-VER: 3.6
NEEDRESTART-KCUR: 6.8.0-124-generic
NEEDRESTART-KEXP: 6.8.0-124-generic
NEEDRESTART-KSTA: 1
NEEDRESTART-SVC: demo-old.service
student@ubuntu:~$ sudo needrestart -r l
Running kernel seems to be up-to-date.

Services to be restarted:
 systemctl restart demo-old.service

KCUR is the running kernel, KEXP the newest installed one, KSTA: 1 means they match and no reboot is needed. SVC is a service to restart.

On Ubuntu the apt hook calls needrestart after every installation, and by default it restarts services by itself — /etc/needrestart/needrestart.conf says so:

text
# UBUNTU: the default restart mode when running as part of the APT
# hook is 'a', unless a specific UI is configured (see below).

Some services it leaves alone even in mode a: their list is the $nrconf{override_rc} key. Such services go to a separate list:

text
student@ubuntu:~$ sudo needrestart -r a
Running kernel seems to be up-to-date.
Restarting services...
Service restarts being deferred:
 systemctl restart demo-lib.service

Here demo-lib is deferred by a line in /etc/needrestart/conf.d/demo.conf:

text
$nrconf{override_rc}{qr(^demo-lib)} = 0;

A deferred service is restarted by hand when it is safe.

Logs

/var/log/unattended-upgrades/ is readable by root and the adm group:

File What it holds
unattended-upgrades.log which packages were installed and why others were skipped
unattended-upgrades-dpkg.log dpkg output during installation
unattended-upgrades-shutdown.log what the service did at shutdown

On the lab machine the directory is empty: the upgrade has never run, and unattended-upgrades.service is off. Timer runs show in journalctl -u apt-daily-upgrade.

Configuration

8

The settings are in apt format in /etc/apt/apt.conf.d/. The files are read in alphabetical order: 20auto-upgrades is the switch, 50unattended-upgrades says what to install and how. Keep your own values in a separate file numbered above 50: a package upgrade may offer to replace 50unattended-upgrades, but it leaves your file alone.

text
// /etc/apt/apt.conf.d/52unattended-upgrades-local
Unattended-Upgrade::Package-Blacklist { "postgresql-"; };
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:30";

A plain value from a later file replaces the earlier one, while list items { … } are added to the earlier ones. apt-config dump shows the result:

bash
apt-config dump | grep -E '^APT::Periodic|^Unattended-Upgrade::'

The main settings are in the directives table below. The machine's 50unattended-upgrades has Automatic-Reboot "false", Remove-Unused-Dependencies "false" and Mail "" commented out — those are the defaults.

Directive What it does Example
APT::Periodic::Update-Package-Lists 20auto-upgrades How often, in days, to refresh the package lists; 0 turns it off. APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade 20auto-upgrades How often, in days, to run unattended-upgrade; 0 means no automatic updates. APT::Periodic::Unattended-Upgrade "1";
Unattended-Upgrade::Allowed-Origins 50unattended-upgrades The list of "origin:suite" sources packages may come from; by default only -security. Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; };
Unattended-Upgrade::Package-Blacklist 50unattended-upgrades Packages not to upgrade; the names are regular expressions. Unattended-Upgrade::Package-Blacklist { "postgresql-"; };
Unattended-Upgrade::Automatic-Reboot 50unattended-upgrades Reboot the machine when an update needs it (/var/run/reboot-required exists). Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time 50unattended-upgrades The reboot time instead of "right after the update". Unattended-Upgrade::Automatic-Reboot-Time "03:30";
Unattended-Upgrade::Remove-Unused-Dependencies 50unattended-upgrades Remove dependencies that are no longer needed, like apt autoremove. Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Mail 50unattended-upgrades The address for the run report; the machine needs working mail delivery. Unattended-Upgrade::Mail "admin@example.org";

Nothing found. Try a shorter query.

Recipes

3

Is it on and when does it run

bash
cat /etc/apt/apt.conf.d/20auto-upgrades
systemctl is-enabled apt-daily.timer apt-daily-upgrade.timer
systemctl list-timers 'apt-daily*'

Both values "1" and both timers enabled mean updates run once a day.

Hold a package back

text
Unattended-Upgrade::Package-Blacklist { "nginx"; "postgresql-"; };

The names are regular expressions: postgresql- matches every package starting with it. Check with sudo unattended-upgrade --dry-run -v: the names show up in the Initial blacklist: line.

Find services running old files

bash
sudo needrestart -r l          # a list, restarts nothing
sudo lsof +L1                  # every open unlinked file
sudo grep -l deleted /proc/[0-9]*/maps 2>/dev/null

Troubleshooting

4

Updates are not installed

  • 20auto-upgrades has "0" or does not exist.
  • The timer is disabled: systemctl is-enabled apt-daily-upgrade.timer.
  • There is no network: the log says failed to download, aborting.
  • The package comes from an origin outside Allowed-Origins or is in Package-Blacklist.

unattended-upgrades.log and --dry-run -v give the reason a package was skipped.

apt waits for a lock: (unattended-upgr)

apt says Could not get lock /var/lib/dpkg/lock-frontend. It is held by process … (unattended-upgr). A scheduled installation is running. Wait for it — systemctl status apt-daily-upgrade. A dpkg process killed in the middle of an installation leaves packages half-installed.

The update is installed, the vulnerability is still there

The service runs an old copy of a file: (deleted) in /proc/<PID>/maps, a line in lsof +L1, the service in needrestart -r l. Restart it. A kernel update needs a reboot: /var/run/reboot-required exists, uname -r is older than the installed kernel.

A service restarted by itself at night

needrestart restarted it after a scheduled update. The time matches the apt-daily-upgrade run, the packages are in unattended-upgrades-dpkg.log. To exclude the service, use the $nrconf{override_rc} key in /etc/needrestart/conf.d/*.conf.

Danger zone

2

Automatic-Reboot "true" without a time

Without Automatic-Reboot-Time the machine reboots right after an update that needs it — in the middle of a working day. Set the time of your maintenance window.

Adding -updates and third-party origins

Security updates change little and rarely break things. The -updates suite and third-party repositories bring new versions, and those would land at night with no check. Update such sources by hand.

See also

  • apt — installing and upgrading packages by hand.
  • systemd-timers — how the apt-daily* timers work.
  • lsof — lsof +L1 and open unlinked files.
  • proc-filesystem — /proc/<PID>/maps and exe.
  • systemctl — restarting services.
  • kernel — a new kernel and the reboot.
Loading…