Skip to content

How to enable automatic security updates

This guide makes a server install security updates on its own every day, and decides what happens when an update needs a reboot.

That's sound practice on Debian, Ubuntu and the RHEL family, whose security updates are small fixes backported to the release you run. It isn't on Arch and Gentoo: there, every update can be a new upstream version, and some need manual steps, so an unattended upgrade can leave the system broken with nobody watching. For those two, this guide automates the check (which updates and security advisories are pending) and leaves the upgrade to you. Patching strategy explains the reasoning.

Before you begin

  • You have a shell with sudo on the machine, and it can reach its package repositories.
  • Decide whether the machine may reboot itself after an update that needs it (a new kernel, or a core library). If services on it can't tolerate an unannounced reboot, keep automatic reboots off and schedule them yourself.

Test on a less important machine first

An automatic update can restart services or, with automatic reboots on, reboot the machine at the configured time. Turn this on for a test or staging machine first, and for production machines once you've seen a few days of updates go through. Have a way to reach the console in case a machine doesn't come back after a reboot.

Steps

  1. Install the tools.

    sudo apt-get update
    sudo apt-get install -y unattended-upgrades
    

    Ubuntu (cloud images and server installations) and Debian's cloud images already have unattended-upgrades installed and enabled; the command does nothing there. A Debian server installed with the Debian installer usually doesn't have it.

    sudo dnf install -y dnf-automatic
    

    checkupdates (from pacman-contrib, which uses fakeroot) lists pending updates without touching the system's package database, and arch-audit lists installed packages with known vulnerabilities:

    sudo pacman -S --needed --noconfirm pacman-contrib fakeroot arch-audit
    

    <!-- verify-setup

    A reader's Gentoo system has a Portage tree; the lab image doesn't.

    emerge-webrsync -q →

    glsa-check (from app-portage/gentoolkit) tests the system against Gentoo Linux Security Advisories, and a cron daemon runs it every day:

    sudo emerge --getbinpkg --noreplace app-portage/gentoolkit sys-process/cronie
    sudo rc-update add cronie default
    sudo rc-service cronie start
    

    With the systemd profile (not verified), use sudo systemctl enable --now cronie.service instead of the last two commands.

  2. Turn on the daily job and choose what it installs.

    Create the file /etc/apt/apt.conf.d/20auto-upgrades with this content, which makes APT refresh its package lists and run unattended-upgrade every day:

    APT::Periodic::Update-Package-Lists "1";
    APT::Periodic::Unattended-Upgrade "1";
    

    Which updates are installed is set by the origin patterns in /etc/apt/apt.conf.d/50unattended-upgrades; the defaults are the ones you want here. Check them:

    apt-config dump | grep -E '^Unattended-Upgrade::(Origins-Pattern|Allowed-Origins)::'
    
    • On Debian, the patterns select the security archive (label=Debian-Security) and the release itself (label=Debian), which receives only point-release updates: tested bug and security fixes.
    • On Ubuntu, Allowed-Origins selects the release pocket (which doesn't change after the release) and the -security pocket, plus the Ubuntu Pro ESM security pockets if the machine is attached to Ubuntu Pro. Ordinary -updates aren't installed automatically.

    To keep a package from being upgraded automatically, add it to Unattended-Upgrade::Package-Blacklist in a file of your own, as in the next step.

    In /etc/dnf/automatic.conf, limit the job to security updates and let it apply them, not only download them:

    sudo sed -i -e 's/^upgrade_type = .*/upgrade_type = security/' -e 's/^apply_updates = .*/apply_updates = yes/' /etc/dnf/automatic.conf
    grep -E '^(upgrade_type|apply_updates|download_updates)' /etc/dnf/automatic.conf
    

    upgrade_type = security installs only updates that carry a security advisory (RHSA on RHEL, ALSA on AlmaLinux, RLSA on Rocky Linux). emit_via = stdio, the default, sends the job's report to the journal.

    Create a service that reports pending updates and fixed vulnerabilities, and downloads the packages so that the update you run later is quick. Create the file /etc/systemd/system/check-updates.service with this content:

    [Unit]
    Description=Report pending package updates and security advisories
    Wants=network-online.target
    After=network-online.target
    
    [Service]
    Type=oneshot
    # checkupdates exits with 2 when there are no updates.
    SuccessExitStatus=2
    ExecStart=/usr/bin/checkupdates --download
    ExecStart=/usr/bin/arch-audit --upgradable
    

    checkupdates --download syncs a temporary copy of the package database and downloads the new packages into pacman's cache; it never changes the installed system or its database, so it can't cause a partial upgrade. arch-audit --upgradable lists installed packages with vulnerabilities that a newer package already fixes.

    Then create a timer for it, /etc/systemd/system/check-updates.timer:

    [Unit]
    Description=Daily check for package updates and security advisories
    
    [Timer]
    OnCalendar=daily
    RandomizedDelaySec=1h
    Persistent=true
    
    [Install]
    WantedBy=timers.target
    

    Create the file /etc/cron.d/glsa-check, which syncs the Gentoo repository every morning and reports the security advisories that affect the system:

    # Sync the Gentoo repository and report the GLSAs that affect this system.
    MAILTO=root
    30 5 * * * root /usr/sbin/emaint --auto sync >/dev/null && /usr/sbin/glsa-check --quiet --test all 2>&1 | grep -v 'not affected'
    

    Cron mails each run's output to root, which needs a working mail transfer agent. glsa-check reports This system is not affected by any of the listed GLSAs even with --quiet, so the grep -v drops that line, and cron sends mail only when there's something to read. emaint --auto sync uses the sync method configured in /etc/portage/repos.conf (or Portage's defaults).

  3. Decide what happens when an update needs a reboot.

    By default unattended-upgrades never reboots: it installs a new kernel, and you reboot when convenient. (On Ubuntu the file /run/reboot-required appears when a reboot is pending.) To let the machine reboot itself at a quiet time when an update requires it, create the file /etc/apt/apt.conf.d/52unattended-upgrades-local:

    // Local settings; later files override 50unattended-upgrades.
    Unattended-Upgrade::Automatic-Reboot "true";
    Unattended-Upgrade::Automatic-Reboot-Time "04:00";
    

    Leave this file out to keep reboots manual.

    By default dnf-automatic never reboots (reboot = never). To reboot when an installed update needs it, such as a new kernel or core library:

    sudo sed -i 's/^reboot = .*/reboot = when-needed/' /etc/dnf/automatic.conf
    

    The reboot is run with reboot_command, by default shutdown -r +5 with a message to logged-in users. Leave the setting at never to keep reboots manual; sudo dnf needs-restarting -r tells you whether one is pending.

    Nothing is installed automatically, so there's nothing to reboot for. After you update, reboot if the update included the kernel package (linux, or the one you use, such as linux-lts): the running kernel stays the old one until you do.

    Nothing is installed automatically, so there's nothing to reboot for. After you update, a reboot is needed if you installed and selected a new kernel.

  4. Enable the schedule.

    The daily runs are started by apt-daily.timer (package lists) and apt-daily-upgrade.timer (upgrades), which are enabled by default. Check:

    systemctl list-timers apt-daily.timer apt-daily-upgrade.timer
    

    The upgrade runs at 06:00 plus a random delay of up to an hour, unless you change the timer with sudo systemctl edit apt-daily-upgrade.timer.

    sudo systemctl enable --now dnf-automatic.timer
    systemctl list-timers dnf-automatic.timer
    

    The timer runs at 06:00 plus a random delay of up to an hour. Don't also enable dnf-automatic-install.timer or the other variants: they do the same job with fixed settings, overriding apply_updates.

    sudo systemctl daemon-reload
    sudo systemctl enable --now check-updates.timer
    systemctl list-timers check-updates.timer
    

    Cronie reads /etc/cron.d/ by itself; no further step is needed.

Verify

Run unattended-upgrade once in dry-run mode, which shows the origins it allows and what it would install, without installing anything:

$ sudo unattended-upgrade --dry-run --debug
...
Allowed origins are: origin=Debian,codename=trixie,label=Debian, origin=Debian,codename=trixie,label=Debian-Security, origin=Debian,codename=trixie-security,label=Debian-Security
...
pkgs that look like they should be upgraded:
...

After the first real run, the log is in /var/log/unattended-upgrades/unattended-upgrades.log, and the packages it installed are listed in /var/log/apt/history.log.

See which security updates are pending, which is what the next run will install:

sudo dnf updateinfo list --security

After the first run, read its report in the journal:

sudo journalctl -u dnf-automatic.service

Run the check once and read its report:

sudo systemctl start check-updates.service
sudo journalctl -u check-updates.service

When the report shows updates, read the Arch Linux news for any manual steps, then update the whole system at once with sudo pacman -Syu, and reboot if the kernel was updated.

Test the system against the advisories now:

$ sudo glsa-check --test all
This system is not affected by any of the listed GLSAs

When advisories affect the system, read the news (eselect news read), then update with sudo emerge --ask --update --deep --newuse @world, merge configuration changes with dispatch-conf, and rebuild or restart what the update affected.

Troubleshooting

Debian or Ubuntu doesn't install anything, although updates are available. Run sudo unattended-upgrade --dry-run --debug and look for the package: it's skipped if its origin doesn't match the patterns, if it's held or blacklisted, or if upgrading it would prompt about a changed configuration file. Check that /etc/apt/apt.conf.d/20auto-upgrades contains both lines with "1".

dnf-automatic downloads updates but doesn't install them. Check apply_updates = yes in /etc/dnf/automatic.conf, and that only dnf-automatic.timer is enabled (systemctl list-timers 'dnf-automatic*'): dnf-automatic-download.timer and dnf-automatic-notifyonly.timer never install.

The machine rebooted unexpectedly. Automatic reboots are on. Check Unattended-Upgrade::Automatic-Reboot (Debian and Ubuntu, with apt-config dump) or reboot in /etc/dnf/automatic.conf, and the times in Automatic-Reboot-Time or the dnf-automatic.timer schedule.

checkupdates fails with Cannot find the fakeroot binary. Install fakeroot, as in step 1.