Automating security updates on a Linux VPS means letting the package manager install security patches on its own schedule while you keep control of reboots, held packages and logging. On Ubuntu and Debian that tool is unattended-upgrades; on AlmaLinux and Rocky Linux it is dnf-automatic. This guide configures both, adds reboot and kernel handling, and shows you how to prove patches actually landed.
Before you start
You need:
- SSH access to the server as
rootor a sudo user. If you are not connected yet, see Connect to your Linux VPS with SSH. - A recent backup or provider snapshot. A patch run can restart services or replace a kernel.
- Knowledge of which distribution you run. Commands below target Ubuntu 22.04/24.04, Debian 12, AlmaLinux 8–9 and Rocky Linux 8–9.
- A rough maintenance window in mind, for example Sunday 04:00, in the server's timezone.
Warning: Automatic updates can restart services mid-request. If you run a database, mail server or panel, test the update path on a staging VPS before enabling it in production.
Step 1: Record a baseline
Write down what is currently pending so you can tell later whether automation worked.
cat /etc/os-release
uname -r
uptimeOn Debian and Ubuntu:
sudo apt update
apt list --upgradableOn AlmaLinux and Rocky Linux:
sudo dnf check-update --security
sudo dnf updateinfo list securitySave the output somewhere outside the server. It is your before-picture.
Step 2: Automate security updates on Ubuntu and Debian
Install the tooling:
sudo apt update
sudo apt install unattended-upgrades apt-listchangesEnable the daily timers by editing /etc/apt/apt.conf.d/20auto-upgrades:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";Now edit /etc/apt/apt.conf.d/50unattended-upgrades. Leave the default Allowed-Origins block alone and adjust the behaviour lines:
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Mail "root";
Unattended-Upgrade::MailReport "on-change";Automatic-Reboot "false" is deliberate. You will handle reboots yourself in Step 4 so a kernel patch never restarts the box at a random hour.
Test the configuration without changing anything:
sudo unattended-upgrade --dry-run --debugThe debug output lists every package it would install and every origin it accepts. If the list is empty, your sources are already current.
Step 3: Automate security updates on AlmaLinux and Rocky Linux
Install the timer:
sudo dnf install dnf-automaticEdit /etc/dnf/automatic.conf:
[commands]
upgrade_type = security
random_sleep = 360
network_online_timeout = 60
download_updates = yes
apply_updates = yes
reboot = never
[emitters]
emit_via = stdioupgrade_type = security limits the run to security errata. random_sleep spreads the load so a fleet of VPS instances does not all hit the mirrors at the same second. reboot = never keeps reboots under your control.
Enable and start the timer:
sudo systemctl enable --now dnf-automatic.timer
systemctl list-timers dnf-automatic.timerCheck what the last run did:
journalctl -u dnf-automatic.service --since "7 days ago"Step 4: Handle kernel updates and reboots
Security patches to the kernel, glibc or systemd only take effect after a restart. Detect that state instead of guessing.
On Ubuntu and Debian, install needrestart so services using old libraries are flagged:
sudo apt install needrestart
sudo needrestart -r lA pending kernel upgrade shows as a file:
cat /var/run/reboot-required 2>/dev/null || echo "no reboot required"On AlmaLinux and Rocky Linux:
sudo dnf needs-restarting -rNow schedule the reboot inside a window. Create /etc/systemd/system/reboot-window.service:
[Unit]
Description=Reboot if a kernel update requires it
[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c 'if [ -f /var/run/reboot-required ]; then /sbin/shutdown -r +5 "Rebooting for kernel update"; fi'Create /etc/systemd/system/reboot-window.timer:
[Unit]
Description=Weekly reboot window
[Timer]
OnCalendar=Sun 04:00
Persistent=true
[Install]
WantedBy=timers.targetThen enable it:
sudo systemctl daemon-reload
sudo systemctl enable --now reboot-window.timerOn RHEL-family systems, replace the ExecStart line with a check on dnf needs-restarting -r:
/usr/bin/bash -c 'if ! /usr/bin/dnf needs-restarting -r >/dev/null 2>&1; then /sbin/shutdown -r +5 "Rebooting for kernel update"; fi'Tip: Reboots are cheap on a VPS. A five-minute restart in a quiet window is far less painful than running a kernel from six months ago.
Step 5: Hold packages that must not move
Some packages break when they jump a major version, for example a database engine or a panel's PHP stack. Pin them.
On Ubuntu and Debian:
sudo apt-mark hold mysql-server php8.3-fpm
apt-mark showholdOn AlmaLinux and Rocky Linux:
sudo dnf install python3-dnf-plugin-versionlock
sudo dnf versionlock add mysql-server
sudo dnf versionlock listHeld packages still receive security patches only if you release the hold and update them manually. Review the list monthly.
Step 6: Confirm logging and alerts
Know where to look when something goes wrong.
- Ubuntu and Debian:
/var/log/unattended-upgrades/unattended-upgrades.logand/var/log/apt/history.log. - AlmaLinux and Rocky Linux:
journalctl -u dnf-automatic.serviceand/var/log/dnf.rpm.log. - Both:
/var/log/secureor/var/log/auth.logfor sudo activity around the update window.
If Unattended-Upgrade::Mail is set, make sure local mail is delivered somewhere you read. A cron job that mails you a weekly summary is often simpler than parsing logs by hand — see Automating server maintenance with cron jobs and shell scripting.
Check it works
- Force a dry run:
sudo unattended-upgrade --dry-run --debugorsudo dnf-automatic --downloadonly. - Confirm the timers are active:
systemctl list-timers | grep -E 'apt-daily|unattended|dnf-automatic'. - Wait one full cycle, then compare against your Step 1 baseline. The security list should be empty or much shorter.
- Check the reboot flag:
cat /var/run/reboot-requiredorsudo dnf needs-restarting -r. - Reboot once on purpose and confirm the server comes back and your services start. If it does not, you want to learn that now, not during an incident.
Troubleshooting
unattended-upgradereports "No packages found that can be upgraded" butapt list --upgradableshows packages. The pending updates are not from a security origin, or the repo is not inAllowed-Origins. Run the dry run with--debugand read the origin lines.- Updates install but the kernel version never changes.
Automatic-Rebootisfalseand no reboot has happened. Check/var/run/reboot-requiredand your reboot timer. dnf-automatic.timernever fires. The timer is disabled or masked. Runsystemctl status dnf-automatic.timerandsystemctl enable --now dnf-automatic.timer.- A service fails after an automatic update. A package moved to a version your config does not support. Hold that package, roll back from your snapshot, and test the upgrade on a staging VPS before releasing the hold.
- The disk fills up with old kernels. Enable
Remove-Unused-Kernel-PackagesandRemove-Unused-Dependencieson Debian-family systems, and checkdnfinstall-only settings on RHEL-family systems.
Next steps
- Review Turn on automatic security updates on Linux for the minimal version of this setup.
- Check Linux release lifecycles so you know when your distribution stops receiving patches entirely.
- Add SSH key rotation so credentials age out on a schedule too.
- If a patch run ever goes wrong in a way that looks like compromise, follow Linux incident response.
