AI Automation

Automate Security Updates With Unattended-Upgrades on Linux VPS

The single most neglected task after provisioning a VPS is keeping packages patched. You set up the firewall, harden SSH, and then forget about updates until the next breach report. Running apt update && apt upgrade manually every week works until it doesn't, one missed week, one published CVE, and your box is in a botnet. This is where unattended-upgrades steps in: it automates only security patches, reboots if necessary, and leaves everything else untouched. By the end of this guide, you'll have a fully automated update pipeline running on Ubuntu 24.04 LTS or Debian 12, with email alerts and scheduled reboots.

  • Key takeaways:
  • Install and enable unattended-upgrades in under five minutes.
  • Restrict updates to only security and stable-update origins, never break production with untested upstream packages.
  • Send failure logs to an email address so you know immediately if a patch cycle failed.
  • Enable automatic reboots only when needed and schedule them to a maintenance window.

Prerequisites

  • A Linux VPS running Ubuntu 24.04 LTS or Debian 12 (the commands are identical for both).
  • Root access or a sudo user.
  • An outbound SMTP relay if you want email notifications (optional).

Why You Need Automatic Patching

Every day, new CVEs are published for the Linux kernel, OpenSSL, systemd, curl, and every library in your stack. The window between a public exploit and your first patch is the only protection you have. Unattended-upgrades compresses that window to hours, not days. It pulls from the security-specific repository, not from the main archive, so you never risk a broader distribution upgrade breaking a running application. On a production Linux VPS, this is the difference between "patched before the scanner arrived" and "compromised over the weekend."

Step 1, Install unattended-upgrades

The package is in the default repositories for both Ubuntu and Debian. Install it with:

sudo apt update
sudo apt install unattended-upgrades apt-listchanges

apt-listchanges is optional but useful: it prints a summary of changelogs for updated packages, which can be emailed alongside the upgrade log. Once installed, verify the daemon is present:

dpkg -l | grep unattended-upgrades

The output should show version 2.12 (Debian 12) or a similar recent build. If nothing appears, re-run the install command.

Step 2, Enable the Automatic Download and Install Timer

Unattended-upgrades relies on apt-daily and apt-daily-upgrade timer units (systemd). Enable the upgrade timer explicitly:

sudo systemctl enable --now apt-daily-upgrade.timer

Check that both timers are active:

sudo systemctl list-timers --all | grep apt

You should see two timers: apt-daily.timer (downloads package lists) and apt-daily-upgrade.timer (installs upgrades). They are staggered so that the download finishes before the upgrade begins. This is the default behavior, do not override the timers unless you have a specific reason.

Step 3, Configure Allowed Origins

The behavior of unattended-upgrades is controlled by the file /etc/apt/apt.conf.d/50unattended-upgrades. Open it:

sudo vi /etc/apt/apt.conf.d/50unattended-upgrades

The critical block is Unattended-Upgrade::Allowed-Origins. By default, only ${distro_id}:${distro_codename}-security is uncommented. That is exactly what you want, only security updates from the official security repo. Do not uncomment ${distro_id}:${distro_codename}-updates unless you explicitly trust all proposed updates, because they can include non-security package bumps that break compatibility with third-party software.

A safe configuration looks like this (the commented lines are defaults):

Unattended-Upgrade::Allowed-Origins {
        "${distro_id}:${distro_codename}-security";
//      "${distro_id}:${distro_codename}-updates";
//      "${distro_id}:${distro_codename}-proposed";
//      "${distro_id}:${distro_codename}-backports";
};

If you also want to pull from updates (e.g., bugfixes that are not security-critical), uncomment that line. On a VPS with full root access, I recommend keeping it commented to minimize surprise breakages. Verify your change by running a dry simulation:

sudo unattended-upgrades --dry-run --debug

This outputs every package it would update, including the origin. If you see only -security lines, the configuration is correct.

Step 4, Configure Automatic Reboots

Some security patches, especially kernel updates, require a reboot to take effect. Unattended-upgrades can do this automatically. Locate these lines in the same file:

//Unattended-Upgrade::Automatic-Reboot "false";
//Unattended-Upgrade::Automatic-Reboot-Time "02:00";

Change them to:

Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";

The reboot only fires if a kernel update was installed. Applications that depend on long-running processes (Node.js, Python web apps, databases) will be interrupted, so schedule the reboot into your maintenance window. If you cannot tolerate any reboot, keep Automatic-Reboot "false" and reboot manually after reviewing the changelog. For most Linux VPS workloads, a 3 AM reboot once or twice a month is harmless.

Step 5, Set Up Email Notifications (Optional but Recommended)

You want to know when a patch cycle fails, silently skipped updates defeat the entire purpose. Unattended-upgrades can send an email on failure. In the same configuration file, find and uncomment:

Unattended-Upgrade::Mail "[email protected]";
Unattended-Upgrade::MailOnlyOnError "true";

The machine needs a working mail command (usually provided by mailutils or heirloom-mailx) and a configured MTA. The simplest approach on a VPS is to install msmtp and point it at an SMTP relay (Gmail, SendGrid, or your own mail server). Install the basic mail stack:

sudo apt install mailutils msmtp msmtp-mta

Then configure ~/.msmtprc (for root) with your relay credentials. After setup, test it:

echo "Test email from unattended-upgrades" | mail -s "Test" [email protected]

If the email arrives, the notification pipeline works. From now on, you will only be emailed when a run fails.

Step 6, Enable the Automatic Update Schedule

Create or update the file /etc/apt/apt.conf.d/20auto-upgrades to control the timer intervals:

sudo dpkg-reconfigure --priority=low unattended-upgrades

This interactive dialog generates 20auto-upgrades with two lines:

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

The "1" means "once per day." You can increase the frequency, but daily is standard and safe. Confirm the file exists:

cat /etc/apt/apt.conf.d/20auto-upgrades

If you prefer to edit the file by hand instead of running the reconfigure dialog, just create it with those two lines.

Troubleshooting

"unattended-upgrades did not run"

Check the systemd timer status first:

systemctl status apt-daily-upgrade.timer

If it is inactive, enable it again. Then check the logs:

journalctl -u apt-daily-upgrade.service --since yesterday

Common causes: the Allowed-Origins block had a syntax error, or the system clock was wrong.

"packages are held back"

Unattended-upgrades will never install a package that is marked hold in dpkg or that has pending configuration changes. Run:

apt-mark showhold

If a package is listed, it will be skipped. Remove the hold with apt-mark unhold if you want it to update automatically.

"email notifications do not arrive"

Test the mail relay independently first. If the test email arrives, the issue is that unattended-upgrades cannot find the mail binary. Ensure mail is in root's PATH (usually /usr/bin/mail) and that Mail in the config file points to a fully qualified address, not a local alias.

FAQ

Will unattended-upgrades break my production application?

It only applies security patches from the official security repository, which are regression-tested. It will not upgrade PHP or PostgreSQL to a new major version, only minor security fixes. The risk is extremely low, but always test on a staging VPS if you can.

Does it work on Debian 12?

Yes. The package and configuration are identical on Debian 12. The timer units are the same. Just ensure you install unattended-upgrades from the main repo and configure Allowed-Origins to ${distro_id}:${distro_codename}-security.

How do I stop unattended-upgrades temporarily?

Stop and disable the timer:

sudo systemctl stop apt-daily-upgrade.timer
sudo systemctl disable apt-daily-upgrade.timer

To re-enable later, run systemctl enable --now apt-daily-upgrade.timer.

Can I exclude specific packages?

Yes, use the Unattended-Upgrade::Package-Blacklist section in 50unattended-upgrades. For example, to never auto-update nginx:

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

Use dpkg -l | grep ^ii | awk '{print $2}' to get the exact package names for the blacklist.

Does it support snap packages or flatpak?

No, unattended-upgrades only handles apt packages. Snaps update automatically via the snap daemon. Flatpak requires manual config. If you use snap on Ubuntu, it is independent and does not conflict.

What happens if an update requires a configuration file merge?

If the update includes a new config file that dpkg cannot merge automatically, the upgrade is held until a human resolves the conflict. The MailOnError flag will send you a notification. Connect via SSH, run sudo dpkg --configure -a, and handle the conflict.

Related articles

Note: This guide is for general reference. Every system and infrastructure has its own specifics, so test each step in a safe environment and consult a qualified engineer before applying it in production.