Unattended-upgrades: patching without the manual grind
How we run automatic security updates on Debian-family servers: package scope, reboot semantics, the log mail that proves the loop works.
There is an oddity in server operations: everyone knows prompt patching matters, and fleets still accumulate security updates for days until someone finds the time. The cause is rarely laziness, it is structure. Manual patching does not scale. It works on ten servers and fails on a hundred. The answer ships with Debian-family servers anyway: unattended-upgrades. This post shows how we set it up sensibly and where its boundaries lie.
What the package actually does
unattended-upgrades is not a magic wand, it is a disciplined runner for apt. Once a day, driven by the apt periodic mechanics, it checks the package sources, selects the updates whose origins are allowed, and installs them. By default that means security updates. That restriction is the value: the installation runs reproducibly, without an unreviewed package mix changing the server overnight. What the package does not do is pull in every upgrade; feature jumps and large intermediate versions stay deliberately outside and follow the normal, supervised change process.
The configuration lives in two files under /etc/apt/apt.conf.d/: one decides whether the mechanism runs at all, the other is named 50unattended-upgrades and defines what is permitted. This separation between enabling and scoping is why the solution behaves so predictably, in small environments and large ones alike.
Reboots: the uncomfortable half of the automation
Automatically installed security updates complete only half the job. Kernels, libc and a few daemons take effect only after a restart or a service restart. For services, restarting the program usually suffices; for the kernel, a real reboot is needed eventually. Configuration decides here: with Automatic-Reboot on and a deep-night time slot, the server handles it itself, or the team plans reboot windows manually and treats the /var/run/reboot-required marker as a mandatory chore. Both paths are legitimate; the inconsistent state, package installed but never effective, is not. So we keep one clear rule per fleet: either automation with a defined window, or a fixed weekly slot that works through the reboot markers.
Those who dislike variable reboot windows can flip the perspective: a server that goes 400 days without a reboot does not collect reliability points, it collects open security lapses.
The mail carrier that carries the proof
The underrated piece of the configuration is the mail. After every run, unattended-upgrades writes a summary of what was installed and what failed. Without a destination address, those messages vanish into the local mailbox black hole that nobody reads. With an address set, a daily stream of mail appears that is worth reading in full during the first months, because that is how a fleet's quirks surface: a repository with occasional signature problems, a server whose disk is too small for larger installs, a package stuck on a configuration question. After two months the patterns are known, the mail gets tuned down to the exceptional level, and it remains the channel that proves the automation is actually running.
This closes the loop with our post on critical security updates: advisories say what is coming, and the log mail shows what actually landed.
Where the automation ends
One sensitive point is how services behave when a package update restarts them. On servers with strict availability expectations we decide deliberately whether services restart automatically or only get flagged. The honest answer depends on the system: a small web server tolerates the automation well, a replicated database wants staged interventions. Automation makes sense where the consequences of a restart are known and harmless; beyond that, the human decision stands.
Equally clear is the boundary for everything beyond a security fix: configuration changes, version switches, stack redesigns. The automatic mechanism handles the recurring routine, not the one-off design questions.
Our default posture
The log that makes the routine visible
Automation that nobody documents turns back into manual work after the next personnel change. So the setup goes into a short page of the fleet documentation: which origins are allowed, which address receives the log mail, who reacts to its exceptions, which time window applies to the reboots. The second component is a half-yearly drill: deliberately simulate one critical vulnerability, from advisory to verified log mail, and measure how many hours the fleet needed. That number is the only one that counts at the next real disclosure, and it is earned in an hour.
The third component is retiring the exception mindset: servers that are unique or short-lived get the same mechanism. The exception that falls out of the pattern otherwise becomes a permanent exception, and after two years patching is back to being a craft.
On the servers we manage the standard state is plain: security updates from the regular sources install themselves daily, the log mail goes to a watched recipient, and reboot handling follows a rule agreed per fleet. The setup pays for itself at every incident: when a vulnerability is published, most of the server fleet is already in the protected state hours later, and nobody had to spend the night on it.
We set this up and audit the existing configuration as part of our Linux administration service; the maintenance-window side of the story pairs naturally with our post on zero-downtime nginx deployment with systemd. If you want your fleet's patching structured this way, we are one message away.
Sources
- manpages.debian.org, unattended-upgrade(8), https://manpages.debian.org/bookworm/unattended-upgrades/unattended-upgrade.8.en.html (accessed 2026-11-04)
- Debian Wiki, UnattendedUpgrades, https://wiki.debian.org/UnattendedUpgrades (accessed 2026-11-04)
- A Debian security advisory as the concrete update example, https://www.debian.org/security/2024/dsa-5803 (accessed 2026-11-04)