The Patching Gap
Patches ship daily. Most administrators apply them weekly at best — often less frequently on servers that run reliably and attract no attention. The window between vulnerability disclosure and active exploitation is shrinking. For critical vulnerabilities in internet-facing services, the median time from CVE publication to observed exploitation is now measured in days, not weeks.
The economics are unambiguous. A compromised server costs orders of magnitude more than the operational overhead of automated patching — in incident response hours, in forensic analysis, in customer notification, in regulatory penalties. The servers that feature in breach reports are rarely exploited via exotic zero-days. They are running known vulnerabilities with patches that were available weeks or months before the intrusion. The fix existed. Nobody applied it.
The case for automated patching has been made elsewhere on this site. This tutorial is the practical companion.
Unattended-upgrades is a Debian and Ubuntu package that applies security patches without human intervention. It checks for updates on a configurable schedule, downloads eligible packages, installs them, and optionally reboots the machine when a kernel patch demands it. The administrator's role shifts from applying patches to monitoring that the automation is working.
Installation
Install unattended-upgrades and apt-listchanges. The latter formats changelogs for upgraded packages and can email them to you.
apt update
apt install unattended-upgrades apt-listchanges
Enable unattended-upgrades.
dpkg-reconfigure -plow unattended-upgrades
Select yes when prompted. This creates the necessary configuration files if they do not already exist and enables the systemd timer that triggers daily runs.
Configuring the Upgrade Policy
The primary configuration file is /etc/apt/apt.conf.d/50unattended-upgrades. This file controls what gets upgraded, how you are notified, and what happens when a reboot is required.
Allowed Origins
Unattended-Upgrade::Allowed-Origins — the package repositories eligible for automatic upgrades. Restrict this to the security pocket only.
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Do not add ${distro_codename}-updates unless you accept that non-security updates — which receive less rigorous testing — may break running services unattended. The security pocket contains only backported security fixes. It is the safest source for automated application.
Package Blacklist
Unattended-Upgrade::Package-Blacklist — packages to exclude from automatic upgrades. Use this for software where an upgrade requires manual validation — database engines, custom kernels, applications with complex migration procedures.
Unattended-Upgrade::Package-Blacklist {
"mysql-server";
"postgresql";
};
Keep this list short. Every package you blacklist is a package that accumulates unpatched vulnerabilities.
Notifications
Unattended-Upgrade::Mail — the email address that receives upgrade notifications.
Unattended-Upgrade::Mail "admin@example.com";
Unattended-Upgrade::MailReport — when to send email. "on-change" sends a report whenever any package is upgraded. "only-on-error" sends mail only when something fails. Use "on-change" — you want a record of every automatic modification to a production system.
Unattended-Upgrade::MailReport "on-change";
Cleanup and Reboots
Unattended-Upgrade::Remove-Unused-Dependencies — set to "true" to remove orphaned packages after upgrades automatically. This is equivalent to apt autoremove and prevents dependency cruft from accumulating.
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot — set to "true" to reboot automatically when a kernel patch or critical library update requires it. On servers with strict uptime requirements, leave this "false" and schedule reboots during maintenance windows. On ephemeral cloud infrastructure behind a load balancer, set it to "true" and let the reboots happen.
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time — the time of day for automatic reboots in 24-hour format. Pick a low-traffic window.
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
This is evaluated in the server's local timezone, not UTC. Set the system timezone explicitly if it differs from your operational timezone.
Configuring the Schedule
The schedule lives in a separate file — /etc/apt/apt.conf.d/20auto-upgrades. This controls how often the package index refreshes and how often upgrades run.
APT::Periodic::Update-Package-Lists — how frequently, in days, to refresh the package index. Set to "1" for daily.
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade — how frequently, in days, to apply eligible upgrades. Set to "1" for daily.
APT::Periodic::Unattended-Upgrade "1";
Both values must be "1" for daily operation. The combination means the system checks for new packages every day and applies security upgrades every day. You can also set APT::Periodic::Download-Upgradeable-Packages to "1" to download packages as soon as they appear, even if the upgrade itself runs later. This reduces the time between patch availability and application.
Email Notifications
The Mail directive requires a working mail transfer agent on the server. Without one, the setting is silently ignored — no error, no warning, no emails. You will believe notifications are configured when they are not.
Install a minimal MTA.
apt install mailutils
On Ubuntu, this pulls in Postfix. During installation, select Internet Site if the server sends mail directly, or Satellite System if it relays through a smarthost. For environments that use an external mail service — SES, SendGrid, a corporate SMTP gateway — configure Postfix as a relay. The relevant directive is relayhost in /etc/postfix/main.cf.
Verify mail delivery manually before relying on it.
echo "Test" | mail -s "Unattended-upgrades test" admin@example.com
If this email does not arrive, neither will your upgrade notifications. Debug the mail system before proceeding. An unmonitored patching system is worse than a manual one — it creates the illusion of coverage.
If you use a cloud provider's managed SMTP service, configure Postfix to authenticate against it. For AWS SES, this means setting relayhost, smtp_sasl_auth_enable, and smtp_tls_security_level in /etc/postfix/main.cf, along with a credentials file in /etc/postfix/sasl/. For SendGrid, the configuration is similar but the relay hostname differs. The provider's documentation covers the specifics. What matters here is that you verify delivery end-to-end before assuming the notification pipeline works.
The Restart Question
A security patch for a userspace library — OpenSSL, glibc, libsystemd — takes effect only when the processes using it are restarted. A patched libssl.so sitting on disk while Nginx serves traffic with the vulnerable in-memory copy provides zero protection. The vulnerability remains exploitable until the process loads the new library.
The needrestart package detects which running services have loaded outdated libraries and handles restarts.
apt install needrestart
By default, needrestart runs interactively — it lists affected services and prompts for confirmation during each apt upgrade. For unattended operation, set it to automatic mode. Edit /etc/needrestart/needrestart.conf.
$nrconf{restart} = 'a';
The a mode restarts affected services automatically without prompting. The l mode lists affected services but takes no action. The i mode prompts interactively. For unattended-upgrades, only a closes the vulnerability window without human intervention.
Kernel patches are different. A new kernel installs alongside the running one, but the system continues executing the old kernel until the next reboot. The Automatic-Reboot directive handles this, but if your uptime constraints prohibit automatic reboots, Canonical Livepatch offers an alternative.
Livepatch applies critical kernel security fixes to the running kernel in memory, without rebooting. It is available to Ubuntu Pro subscribers — free for personal use on up to five machines.
pro attach <your-token>
pro enable livepatch
Livepatch does not eliminate reboots entirely. Some kernel changes are too structural for live patching. But it covers the most critical security fixes and extends the window before a reboot becomes operationally necessary from hours to weeks.
Testing
Run a dry test before trusting unattended-upgrades in production.
unattended-upgrade --dry-run --debug
This simulates the upgrade process without installing anything. The debug output shows which packages would be upgraded, which would be skipped, and the reason for each decision. Review it carefully.
Common reasons for packages being skipped include: the package appears in the blacklist, the package comes from a repository not listed in Allowed-Origins, or the package has unresolved dependencies that would require installing non-security packages.
After verifying the dry run, force an actual run to confirm end-to-end operation.
unattended-upgrade -v
Check that the expected packages were upgraded, that email notification arrived, and that needrestart handled any required service restarts.
Monitoring
Unattended-upgrades writes to two log files.
/var/log/unattended-upgrades/unattended-upgrades.log contains a summary of each run — which packages were upgraded, which were skipped, and whether a reboot is pending.
/var/log/unattended-upgrades/unattended-upgrades-dpkg.log contains the raw dpkg output from each upgrade, including any errors or warnings from package maintainer scripts.
Monitor both. A monitoring system that alerts on log absence is more valuable here than one that alerts on content. If the summary log has not been written to in 48 hours, unattended-upgrades is not running and your server is accumulating unpatched vulnerabilities silently.
A pending reboot is indicated by the presence of /var/run/reboot-required. Monitor this file. If automatic reboots are disabled, schedule the reboot manually — a kernel patch that sits waiting for a reboot is a kernel patch that is not protecting you.
Check the systemd timer that drives unattended-upgrades.
systemctl status apt-daily-upgrade.timer
If this timer is not active, unattended-upgrades will never run regardless of what the configuration files say. On some minimal server images and container-based deployments, the timer is not enabled by default. Enable it explicitly.
systemctl enable --now apt-daily-upgrade.timer
You can also check the timestamp of the last successful run.
stat /var/log/unattended-upgrades/unattended-upgrades.log
If the file is older than 48 hours, investigate. If it does not exist, unattended-upgrades has never completed a run on this machine.
Edge Cases
Held packages are skipped. If you have pinned a package with apt-mark hold, unattended-upgrades respects the hold. This is correct behaviour — but audit your holds periodically. A package held for a compatibility reason two years ago may now be blocking critical security fixes that have long since been validated against your stack.
Third-party repositories are not included by default. Packages from PPAs, vendor repositories, or internal apt mirrors are upgraded only if their origin is explicitly added to Allowed-Origins. Do not add arbitrary PPAs to Allowed-Origins. PPA packages do not receive the same security review as Ubuntu's official repositories, and an unattended PPA upgrade can introduce breaking changes with no warning and no rollback path.
To include a specific vendor repository — Docker's official apt repository is a reasonable example — identify its origin with apt-cache policy <package> and add the matching origin string to Allowed-Origins.
Phased updates on newer Ubuntu releases mean not all machines receive a given update simultaneously. Canonical rolls out updates in phases to detect regressions across the fleet before full deployment. Unattended-upgrades honours this phasing by default. Your machine may lag a day behind the published advisory. This is acceptable — the small delay provides regression protection that outweighs the marginal increase in exposure window.
Disk space is an overlooked failure mode. Unattended-upgrades downloads packages before installing them. If /var/cache/apt/archives/ fills the partition, the download fails silently and no upgrades are applied. Monitor disk usage on the root and /var partitions. Set APT::Periodic::AutocleanInterval to "7" in 20auto-upgrades to clear the package cache weekly.
Unattended-upgrades and configuration file conflicts. When a package upgrade includes a changed configuration file, dpkg normally prompts for a decision — keep the local version or install the maintainer's version. In unattended mode, the default is to keep the local version. This is almost always correct. If you need to override this for a specific package, add it to the Dpkg::Options configuration rather than changing the global behaviour.
Operational Summary
Install unattended-upgrades and apt-listchanges, restrict Allowed-Origins to the security pocket, configure email notifications with MailReport set to on-change, set daily update checks and daily upgrade application in 20auto-upgrades, install needrestart in automatic mode so library-dependent services restart without prompting, decide whether automatic reboots at 03:00 suit your environment or whether Livepatch is the better path, verify with unattended-upgrade --dry-run --debug, and monitor /var/log/unattended-upgrades/ and /var/run/reboot-required continuously. A server that patches itself is not a server you stop watching — it is a server that handles the routine so you can focus on the exceptions.