Schedule automatic security updates on Linux, BSD and macOS
Most systems can install security updates on their own (automatic updates). The useful part is choosing when: a fixed time in a quiet hour, with any reboot in the same window. This page gives the exact timer, cron or config change for each system. Whether to install automatically at all is covered in Automatic updates.
| System | Tool | Default run time | Reboots by itself? |
|---|---|---|---|
| Debian, Ubuntu | unattended-upgrades via apt-daily-upgrade.timer | 06:00 plus up to 60 min | Only with Automatic-Reboot |
| RHEL, Alma, Rocky, Fedora | dnf-automatic / dnf5 automatic | 06:00 plus up to 60 min | Only with reboot=when-needed |
| SUSE | zypper patch from your own timer | None until you add one | No |
| Alpine | apk from /etc/periodic/daily | 02:00 (default crontab) | No |
| FreeBSD, OpenBSD, NetBSD | cron, periodic, daily.local | None until you add one | No |
Rules for safe schedules
- Pick one maintenance window per group of servers, and run the reboot in the same window as the install.
- Update a small canary group a day before the rest. If it breaks, you stop the rollout.
- Never reboot every member of a cluster at the same time. Give each node a different time.
- Keep console or out-of-band access for remote machines, and a snapshot or backup to roll back to.
- Read the logs after each run. A timer that fails quietly leaves you unpatched.
After updates, services may still run old code. See Restarts and reboots and Kernel updates.
Debian and Ubuntu
unattended-upgrades is started by two systemd timers. apt-daily.timer downloads at 06:00 and 18:00 with up to 12 hours of random delay. apt-daily-upgrade.timer installs at 06:00 with up to 60 minutes of delay. Turn it on (Ubuntu Server has it on by default):
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades # answer yes
cat /etc/apt/apt.conf.d/20auto-upgrades
# APT::Periodic::Update-Package-Lists "1";
# APT::Periodic::Unattended-Upgrade "1";
To install at 02:30 instead, override the timer. The empty OnCalendar= line clears the default first:
sudo systemctl edit apt-daily-upgrade.timer
[Timer]
OnCalendar=
OnCalendar=*-*-* 02:30
RandomizedDelaySec=15m
systemctl list-timers 'apt-daily*' # shows the next run
Debian asks you to keep some random delay so that not every machine hits the mirrors at once. The timers have Persistent=true, so a machine that was off at 02:30 runs the job soon after it boots. To prevent that, add Persistent=false to the same override.
For the reboot, put these lines in /etc/apt/apt.conf.d/52unattended-upgrades-local:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:30";
Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
The server reboots without confirmation at 03:30, but only if an update created /var/run/reboot-required. The time is passed to shutdown, so “now” and “+30” (minutes) also work. More detail is on the Debian and Ubuntu pages.
RHEL, AlmaLinux, Rocky and Fedora
RHEL 8, 9 and 10 and rebuilds (DNF 4)
sudo dnf install dnf-automatic
sudoedit /etc/dnf/automatic.conf
# [commands]
# upgrade_type = security
sudo systemctl enable --now dnf-automatic-install.timer
There are four timers. dnf-automatic-install.timer downloads and installs, dnf-automatic-download.timer only downloads, and dnf-automatic-notifyonly.timer only reports. dnf-automatic.timer follows apply_updates in the config file. All of them run at 06:00 with up to 60 minutes of delay. Change the time the same way as on Debian:
sudo systemctl edit dnf-automatic-install.timer
[Timer]
OnCalendar=
OnCalendar=*-*-* 02:30
RandomizedDelaySec=15m
To reboot after kernel or systemd updates, set reboot = when-needed in the [commands] section. The option was added in DNF 4.15, so check man dnf-automatic on older minor releases. The default reboot_command is shutdown -r +5, a reboot five minutes after the run. See RHEL and rebuilds.
Fedora 41 and later (DNF5)
sudo dnf install dnf5-plugin-automatic
sudoedit /etc/dnf/automatic.conf
# [commands]
# apply_updates = True
# upgrade_type = security
# reboot = when-needed
sudo systemctl enable --now dnf5-automatic.timer
The differences matter. DNF5 has no -install, -download or -notifyonly timers: there is one timer (dnf5-automatic.timer, also installed as dnf-automatic.timer), and the config file decides what it does. apply_updates defaults to no, so without it updates are only downloaded. The shipped defaults are in /usr/share/dnf5/dnf5-plugins/automatic.conf, and your /etc/dnf/automatic.conf overrides them. Override the schedule with sudo systemctl edit dnf5-automatic.timer.
SUSE Linux Enterprise and openSUSE Leap
SUSE has no built-in unattended update switch, so add a small script and a systemd timer. Create /usr/local/sbin/zypper-security-update (mode 0755):
#!/bin/sh
# Exit code 103: zypper updated itself and must run again.
for run in 1 2; do
zypper --non-interactive patch --category security \
--with-interactive --auto-agree-with-licenses
rc=$?
[ "$rc" -eq 103 ] || break
done
# 0 = done, 102 = done and a reboot is suggested
[ "$rc" -eq 0 ] || [ "$rc" -eq 102 ] || exit "$rc"
zypper --quiet needs-rebooting
if [ $? -eq 102 ]; then
logger -t zypper-security-update "reboot suggested"
# shutdown -r 03:30 # uncomment to REBOOT automatically
fi
exit 0
--with-interactive stops zypper from skipping patches that need a reboot (such as kernel fixes). --auto-agree-with-licenses accepts license prompts, which would otherwise stop an unattended run. Then add /etc/systemd/system/zypper-security-update.service:
[Unit]
Description=Install SUSE security patches
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/zypper-security-update
And /etc/systemd/system/zypper-security-update.timer:
[Unit]
Description=Nightly SUSE security patches
[Timer]
OnCalendar=*-*-* 02:30
RandomizedDelaySec=15m
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now zypper-security-update.timer
journalctl -u zypper-security-update.service # read the last run
Run zypper patch-check now and then to see whether any patch still waits for an interactive decision. Immutable editions (SUSE Linux Micro, Leap Micro) use transactional updates instead.
Alpine Linux
The default root crontab runs every script in /etc/periodic/daily at 02:00. Create /etc/periodic/daily/apk-upgrade (no file extension):
#!/bin/sh
apk upgrade -U >>/var/log/apk-upgrade.log 2>&1
chmod +x /etc/periodic/daily/apk-upgrade
rc-update add crond default
rc-service crond start
Run these as root. To use a different time, edit the /etc/periodic/daily line with crontab -e. apk does not restart services or flag a reboot. After an update to the kernel package (linux-lts or linux-virt), reboot in your window.
Arch Linux
Unattended full upgrades are risky on Arch. Some updates need manual steps that are announced only in the news. pacman leaves .pacnew files for you to merge. Partial upgrades are not supported, so a failed run can leave the system half-upgraded with nobody watching. Get notified instead, and upgrade by hand with sudo pacman -Syu after reading the news. checkupdates (package pacman-contrib) lists pending updates without touching the system. It exits with 2 when there are none:
# /etc/systemd/system/checkupdates.service
[Service]
Type=oneshot
ExecStart=/usr/bin/checkupdates
SuccessExitStatus=2
# /etc/systemd/system/checkupdates.timer
[Timer]
OnCalendar=*-*-* 08:00
Persistent=true
[Install]
WantedBy=timers.target
Enable it with sudo systemctl enable --now checkupdates.timer and read the list with journalctl -u checkupdates, or send it on through your monitoring.
FreeBSD
Add these lines to /etc/crontab (note the user column):
@daily root freebsd-update cron
30 3 * * 0 root pkg upgrade -y >>/var/log/pkg-upgrade.log 2>&1
freebsd-update cron waits a random time of up to an hour, downloads base-system updates and mails root. It does not install them. Run freebsd-update install yourself in the window. The second line upgrades packages every Sunday at 03:30 without asking. pkg audit already runs in the daily security report by default. Add weekly_status_pkg_enable="YES" to /etc/periodic.conf to also get a weekly list of outdated packages. A kernel update needs a reboot: if freebsd-version -k (installed) differs from freebsd-version -r (running), reboot. Restart services whose packages changed. On pkgbase systems (FreeBSD 15 and later) the base system is updated with pkg instead. See FreeBSD.
OpenBSD
Root's crontab runs /etc/daily at 01:30, which runs /etc/daily.local first and mails any output to root. To install errata and package updates every night, put this in /etc/daily.local:
syspatch
pkg_add -u -I
-I forces non-interactive mode. syspatch installs all missing binary patches for the base system, since patches are cumulative. To be told instead of patched, use syspatch -c, which only lists missing patches. A kernel erratum needs a reboot, so read the daily mail and reboot in your window. See OpenBSD.
NetBSD
The daily run (04:15 in the default crontab) can download the pkgsrc vulnerability list, and the security run then reports vulnerable packages. Turn the download on in /etc/daily.conf:
fetch_pkg_vulnerabilities=YES
To upgrade packages weekly, add to root's crontab (crontab -e):
30 3 * * 0 /usr/pkg/bin/pkgin -y upgrade >>/var/log/pkgin-upgrade.log 2>&1
-y answers yes to prompts. Do not automate base-system updates with sysupgrade, since it fetches new sets and reboots. Plan those by hand. Other BSDs are under Unix and BSD.
macOS
sudo softwareupdate --schedule on turns on the background check for updates, and softwareupdate --schedule shows whether it is on. It does not install anything or set a time. Check man softwareupdate on your own release before you rely on it. The options to download and install updates automatically are in System Settings, under General, Software Update. On Apple silicon, an unattended softwareupdate --install needs an owner user's credentials (--user and --stdinpass), so scripting it from launchd means storing a password. Use MDM instead.
Managed Macs. Apple deprecated the old com.apple.SoftwareUpdate MDM profile in macOS 26 and removed it in macOS 27. With an MDM server, use the declarative com.apple.configuration.softwareupdate.settings configuration (automatic actions, deferrals) and software update enforcement with a deadline. Background Security Improvements arrive between updates for the latest macOS and may need a restart.
MikroTik RouterOS
RouterOS has its own scheduler. A nightly script can run /system package update check-for-updates and install new versions at a set time. Every install reboots the router, and RouterBOARD firmware needs a second reboot. The full script, with its warnings, is in MikroTik RouterOS.
Windows is out of scope for this site.
Sources
- UnattendedUpgrades, Debian Wiki
- Automatic updates, Ubuntu Server documentation
- DNF Automatic, DNF documentation
- DNF release notes (reboot option, 4.15.0)
- dnf5 automatic, DNF5 documentation
- dnf5.spec (dnf5-plugin-automatic files), DNF5 project
- zypper(8) manual page, openSUSE
- Default root crontab (alpine-baselayout), Alpine aports
- checkupdates(8) manual page, Arch Linux
- freebsd-update(8) manual page, FreeBSD
- periodic.conf(5) manual page, FreeBSD
- freebsd-version(1) manual page, FreeBSD
- 410.pkg-audit periodic script, pkg project
- daily(8) manual page, OpenBSD
- syspatch(8) manual page, OpenBSD
- pkg_add(1) manual page, OpenBSD
- daily.conf(5) manual page, NetBSD
- security.conf(5) manual page, NetBSD
- pkgin(1) manual page source, pkgin project
- softwareupdate command reference, SS64
- About software updates, Apple Platform Deployment
- SoftwareUpdate profile (deprecated), Apple Developer
- SoftwareUpdateSettings declaration, Apple Developer
- Scheduler, RouterOS manual