Linux Unattended Upgrades: Patch Automatically, Reboot Deliberately
Last edited on August 5, 2026

unattended-upgrades can install eligible APT updates without waiting for an administrator. It cannot decide whether a service may restart during business hours, whether a kernel reboot is safe, or whether the application came back correctly. Those are separate operating decisions.

For a Debian or Ubuntu server, a dependable policy has four boundaries: which packages are eligible, how affected services restart, who owns a requested reboot, and what proves the workload recovered. Keep package installation automatic where the risk supports it, but never let “the upgrade command finished” become the only success signal.

This guide is for a site owner, developer, or junior administrator with sudo access and basic SSH familiarity. Commands were reproduced with unattended-upgrades 2.12 on Debian 13; current Ubuntu documentation supplies Ubuntu-specific defaults and needrestart behavior.

Automation Stops at the Reboot Boundary

APT installs new files on disk. Running processes may still hold older libraries in memory, and a running kernel remains the old kernel until boot. A marker such as /run/reboot-required means the package workflow requests a reboot; it does not say when downtime is acceptable or whether the next boot will succeed.

Ubuntu’s current automatic-updates documentation makes the separation explicit. Automatic reboot defaults to false. If an administrator sets it to true, unattended-upgrades may reboot without confirmation after a run that requests one. A configured clock time is interpreted in the server’s local timezone and passed to shutdown.

Service restarts create another boundary. Starting with Ubuntu 24.04 LTS, needrestart restarts affected services automatically by default, while known exclusions remain. A database, queue worker, or custom daemon may need a workload-specific policy; blindly forcing every service to restart is not a universal safe default.

Operating choice Package owner Reboot owner Best fit Required proof
Host installs; operator reboots unattended-upgrades maintenance owner or orchestrator stateful systems, clustered services, strict windows pending-reboot alert, planned drain, external acceptance
Host installs and reboots unattended-upgrades the same host small recoverable systems with tested boot behavior console path, deliberate rehearsal, post-boot monitor

Neither row is automatically better. The unsafe choice is leaving reboot ownership unnamed.

Audit the Policy You Already Have

Do not begin by pasting a configuration from another distribution. Debian commonly uses Origins-Pattern; Ubuntu documents Allowed-Origins. Release images, cloud vendors, local overrides, and configuration management may also have changed the shipped files.

Start with the installed release and package version:

. /etc/os-release
printf 'OS=%s VERSION=%s\n' "$ID" "$VERSION_ID"
dpkg-query -W -f='${Package} ${Version}\n' unattended-upgrades 2>/dev/null || true

The reproduced host returned:

OS=debian VERSION=13
unattended-upgrades 2.12

If the second line is absent, install the package first. Then enable the two periodic APT jobs with an explicit local file instead of relying on an image default:

sudo apt-get update
sudo apt-get install --yes unattended-upgrades
test ! -e /etc/apt/apt.conf.d/51-voxfor-auto-upgrades || \
  sudo cp -a /etc/apt/apt.conf.d/51-voxfor-auto-upgrades \
    /root/51-voxfor-auto-upgrades.before-enable
printf '%s\n' \
  'APT::Periodic::Update-Package-Lists "1";' \
  'APT::Periodic::Unattended-Upgrade "1";' | \
  sudo tee /etc/apt/apt.conf.d/51-voxfor-auto-upgrades >/dev/null
sudo chmod 0644 /etc/apt/apt.conf.d/51-voxfor-auto-upgrades

Update-Package-Lists "1" refreshes package metadata daily; Unattended-Upgrade "1" asks APT’s periodic job to apply eligible updates daily. Installation alone does not prove scheduling is enabled. The later effective-value and timer checks are the success test.

The 51-voxfor-auto-upgrades name is dedicated to this change, so it does not erase unrelated periodic settings in a distribution or image file. If enabling fails validation, restore /root/51-voxfor-auto-upgrades.before-enable when it exists. If this workflow created the dedicated file from scratch, move /etc/apt/apt.conf.d/51-voxfor-auto-upgrades aside instead. Then rerun apt-config dump; an incomplete local policy must not remain active.

Next, inspect values APT actually merged from /etc/apt/apt.conf.d/:

apt-config dump | grep -E \
  '^(APT::Periodic|Unattended-Upgrade::(Allowed-Origins|Origins-Pattern|Automatic-Reboot))'

On the reproduced Debian host, the decisive lines included:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
Unattended-Upgrade::Origins-Pattern:: "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";

An origin identifies a repository that may supply packages. A pin changes APT’s preference among versions, while a hold prevents an installed package from being upgraded. Missing periodic settings mean the package may exist without scheduled application. Before changing an existing state, save the current configuration and record who manages it:

test ! -e /etc/apt/apt.conf.d/51-voxfor-auto-upgrades || \
  sudo cp -a /etc/apt/apt.conf.d/51-voxfor-auto-upgrades \
    /root/51-voxfor-auto-upgrades.before-policy
test ! -e /etc/apt/apt.conf.d/50unattended-upgrades || \
  sudo cp -a /etc/apt/apt.conf.d/50unattended-upgrades \
    /root/50unattended-upgrades.before-policy
sudo grep -Rns 'Unattended-Upgrade\|APT::Periodic' /etc/apt/apt.conf.d/

On a newly built image, separate provisioning from readiness. A completed cloud-init run can still leave package locks, failed services, or network dependencies; use the new-host readiness diagnosis before declaring the patch policy active.

Confirm the scheduler, not an assumed hour

Modern Debian and Ubuntu installations normally use apt-daily.timer and apt-daily-upgrade.timer. List the real schedule and unit definitions:

systemctl list-timers --all 'apt-daily*'
systemctl cat apt-daily.timer apt-daily-upgrade.timer
systemctl show apt-daily-upgrade.timer \
  -p RandomizedDelayUSec -p Persistent

Representative timer evidence looked like this; dates will differ on every host:

NEXT                        LEFT LAST                         PASSED UNIT
Thu 2026-08-06 06:44:48 UTC   8h Wed 2026-08-05 06:43:27 UTC 15h ago apt-daily-upgrade.timer
RandomizedDelayUSec=1h
Persistent=yes

RandomizedDelaySec and Persistent spread load across package mirrors and allow a missed run to catch up after the machine starts. Consequently, a timer that says 06:00 may run later, and an offline server may begin package work shortly after boot. The systemd scheduling contract comparison explains why catch-up behavior and dependency ordering matter more than a familiar clock expression.

Record NEXT, LAST, RandomizedDelaySec, and Persistent as the baseline. A maintenance plan that ignores any of those fields is not describing the timer that will run.

Check for an existing reboot request

Use the canonical marker before changing policy:

if test -e /run/reboot-required; then
  echo 'reboot requested'
  sed -n '1,40p' /run/reboot-required.pkgs 2>/dev/null || true
else
  echo 'no reboot requested'
fi

The package list explains what requested the marker; it is not a complete vulnerability assessment. Preserve it with the maintenance record, then decide the reboot under the same recovery rules used for any other production change.

Choose One of Two Reboot Owners

For stateful services, multi-node systems, or strict customer windows, leave host-owned reboot disabled. Preserve any previous local policy first:

test ! -e /etc/apt/apt.conf.d/52-reboot-policy || \
  sudo cp -a /etc/apt/apt.conf.d/52-reboot-policy \
    /root/52-reboot-policy.before-change

Then use the later-numbered APT drop-in so the ownership decision is visible without rewriting the vendor file:

// /etc/apt/apt.conf.d/52-reboot-policy
Unattended-Upgrade::Automatic-Reboot "false";

An external monitor must then alert on /run/reboot-required, and a named operator or orchestrator must drain traffic, reboot, and verify recovery. Disabling automatic reboot without a monitored queue simply turns a controlled delay into indefinite patch debt.

Small, recoverable hosts can let the local policy own the reboot after one deliberate rehearsal. A cautious example is:

// /etc/apt/apt.conf.d/52-reboot-policy
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
Unattended-Upgrade::Automatic-Reboot-Time "03:30";

Automatic-Reboot-WithUsers "false" may defer a reboot while users are logged in. It is not a maintenance lock: abandoned sessions can block action, while no logged-in session says nothing about customers, jobs, transactions, or replicas. Use workload monitoring and an explicit window as the real guard.

Validate scalar values after saving the file:

apt-config dump | grep -E \
  '^Unattended-Upgrade::Automatic-Reboot(|-WithUsers|-Time) '

A drop-in is a small local configuration file loaded after the packaged defaults. It keeps the ownership change easy to identify and undo. Success means apt-config dump prints exactly the chosen reboot values. To roll back this policy, restore the predecessor when /root/52-reboot-policy.before-change exists:

sudo cp -a /root/52-reboot-policy.before-change \
  /etc/apt/apt.conf.d/52-reboot-policy
apt-config dump | grep -E \
  '^Unattended-Upgrade::Automatic-Reboot(|-WithUsers|-Time) '

If no predecessor existed because this workflow created the file, move only the new drop-in aside, then check the effective values again:

sudo mv /etc/apt/apt.conf.d/52-reboot-policy \
  /root/52-reboot-policy.disabled
apt-config dump | grep -E \
  '^Unattended-Upgrade::Automatic-Reboot(|-WithUsers|-Time) '

Before either model goes live, retain a second administrative path. Tailscale SSH access controls can reduce direct SSH exposure, but an out-of-band provider console remains important because the overlay, firewall, or network stack may be the component that fails after boot.

Create a Window the Timers Can Actually Meet

Changing only Automatic-Reboot-Time does not move the upgrade job. The upgrade timer, its random delay, package download time, dpkg configuration, and service restarts all occur before the reboot decision.

When the default timer conflicts with backups or peak traffic, preserve an existing local override first, then create a drop-in instead of editing /usr/lib/systemd/system/apt-daily-upgrade.timer:

test ! -e /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf || \
  sudo cp -a /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf \
    /root/apt-daily-upgrade.timer.override.before-policy
sudo systemctl edit apt-daily-upgrade.timer

Enter:

[Timer]
OnCalendar=
OnCalendar=*-*-* 02:15:00
RandomizedDelaySec=30m
Persistent=true

The empty OnCalendar= clears inherited list entries before the new calendar is added. Reload unit metadata, restart only the timer, and verify the next run:

sudo systemctl daemon-reload
sudo systemctl restart apt-daily-upgrade.timer
systemctl list-timers --all apt-daily-upgrade.timer
systemctl cat apt-daily-upgrade.timer

Verification must show the intended next window and the local override.conf beneath the packaged unit. If that window is wrong, restore /root/apt-daily-upgrade.timer.override.before-policy when it exists. If this workflow created the override from scratch, roll back only that file:

sudo mv /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf \
  /root/apt-daily-upgrade.timer.override.disabled
sudo systemctl daemon-reload
sudo systemctl restart apt-daily-upgrade.timer
systemctl list-timers --all apt-daily-upgrade.timer
systemctl cat apt-daily-upgrade.timer

Do not restart apt-daily-upgrade.service merely to refresh the timer; starting the service can perform package work. In this example, the job may begin between 02:15 and 02:45. A 03:30 reboot leaves 45 minutes after the latest scheduled start, but 45 minutes is not a guarantee. Measure the actual host, allow for slow mirrors and dpkg work, and widen the window when the workload requires it.

If package work is still running when 03:30 passes, unattended-upgrades does not interrupt dpkg just to meet the clock. A requested reboot can occur after package work completes, so the real downtime window must include measured upgrade variance rather than treating the configured reboot time as a deadline.

Fleet operators should avoid letting every node make the same local decision. A canary is one representative host patched first. A bounded batch is a deliberately limited group patched next while healthy peers keep serving traffic. To drain traffic is to stop sending new requests to the host and allow active work to finish. An orchestrator is the external automation that drains traffic, sequences those groups, stops on failure, and records acceptance. The success state is a healthy canary plus an external business check before the next batch begins.

Dry-Run the Package Decision

A dry run validates configuration parsing and package eligibility without installing packages. The Debian manual for unattended-upgrade(8) defines --dry-run as simulation and --debug as detailed diagnostic output.

Run it after origin, blacklist, or schedule-policy changes:

set -o pipefail
sudo unattended-upgrade --dry-run --debug 2>&1 | \
  tee /tmp/unattended-upgrade-dry-run.log
dry_run_status=${PIPESTATUS[0]}
test "$dry_run_status" -eq 0

pipefail and PIPESTATUS[0] preserve the upgrade command’s exit status; without them, a successful tee could hide a failed simulation. The reproduced evidence was:

Allowed origins are: origin=Debian,codename=trixie,label=Debian, ... label=Debian-Security
Option --dry-run given, *not* performing real actions
Packages that will be upgraded: libaom3
All upgrades installed
upgrade result: True All upgrades installed

Read four parts of the result:

  1. Allowed origins are or origin-matching debug lines must name only repositories your policy owns.
  2. Rejected third-party or non-allowed origins should remain ineligible unless explicitly approved.
  3. Packages that will be upgraded may be empty on a fully patched host; that is a valid steady state.
  4. Option --dry-run given, *not* performing real actions proves simulation mode was active.

During reproduction on Debian 13, the dry run selected one package from Debian-Security, pinned third-party and non-allowed candidates out of the transaction, and finished successfully without changing installed packages. That evidence is stronger than a copied configuration because it shows what the current host would select.

Package holds and blacklists deserve separate review. Do not blacklist databases, kernels, or container runtimes just because another guide does. A hold transfers patch responsibility to another owner; document the vendor policy, compatibility test, deadline, and manual maintenance path before creating it. If the simulation exits nonzero or shows an unexpected origin, stop: restore the relevant saved APT file or move the new local file aside, then rerun apt-config dump and the dry run before scheduling anything.

Prove the Server Can Return

Dry-run success ends before the highest-risk transition. Rehearse one controlled reboot while someone can watch the console and while rollback evidence is available.

Preparation should include:

  • an off-host backup whose restore path is known;
  • console access independent of SSH and the guest network;
  • enabled boot-time units for every essential service;
  • a current firewall and network configuration backup;
  • a named rollback decision and maximum outage time;
  • an external check that exercises the real customer or API path.

Where platform-level recovery is available, a timed VM restore drill is better evidence than a snapshot icon. Keep application data consistency in scope; a crash-consistent image is not automatically a transaction-consistent recovery.

Inspect essential units before the rehearsal:

systemctl is-enabled nginx.service
systemctl is-enabled your-app.service
systemctl --failed --no-pager
sudo reboot

After reconnecting, verify boot identity, network, services, and the application—not just SSH:

uptime -s
uname -r
systemctl --failed --no-pager
systemctl is-active nginx.service your-app.service
curl --fail --silent --show-error https://example.com/health

Replace unit names and the URL with real workload checks. A host that boots but waits indefinitely for one interface needs the network readiness ownership trace. A daemon that repeatedly crashes and stops being retried belongs in the systemd start-limit evidence path. Those are recovery branches, not reasons to call the patch run successful.

Read the Next Run as Evidence

After the first unattended window, collect a small, repeatable receipt:

sudo journalctl -u apt-daily-upgrade.service --since '24 hours ago' --no-pager
sudo tail -80 /var/log/unattended-upgrades/unattended-upgrades.log
systemctl list-timers --all apt-daily-upgrade.timer
test -e /run/reboot-required && echo 'reboot pending' || echo 'no reboot pending'

Look for the run start, effective origins, selected packages, dpkg errors, service-restart consequences, reboot request, and next scheduled attempt. A clean log plus a failing external health check is still a failed maintenance result.

Automation should raise an alert when the timer has not run within policy, the upgrade exits unsuccessfully, a reboot request exceeds its deadline, or the post-boot workload check fails. For local-host restarts, also review current needrestart behavior. Ubuntu documents service-specific exclusions through /etc/needrestart/conf.d/; use those only for services whose maintenance boundary is understood, and plan how their old in-memory code will eventually restart.

FAQ: Questions That Decide the Policy

Does unattended-upgrades reboot Linux automatically?

Not by default on Ubuntu. It reboots automatically only when Unattended-Upgrade::Automatic-Reboot is enabled and the completed upgrade requests a reboot. The configured time uses the server’s local timezone, so verify it with timedatectl and the effective APT values.

Which updates will unattended-upgrades install?

Only packages that match the host’s effective allowed origins or origin patterns, plus normal APT policy such as pins and holds. Debian and Ubuntu defaults differ. Inspect apt-config dump and run unattended-upgrade --dry-run --debug on the actual host instead of assuming another server’s file applies.

Does /run/reboot-required mean the server is currently insecure?

The marker means an installed update requests a reboot before its full effect is available. It does not measure exploitability or business impact. Treat it as actionable patch debt: record triggering packages, choose a deadline from risk, reboot under a recovery plan, and verify the running kernel and workload afterward.

Can logged-in users safely prevent an automatic reboot?

Automatic-Reboot-WithUsers "false" can defer a reboot while users are logged in, but login state is not a reliable maintenance lock. Stale sessions may delay patches, and an empty login list does not prove customers or background jobs are idle. Use a real maintenance window and workload checks.

Should databases and kernels be blacklisted from automatic updates?

Not automatically. A blacklist or hold moves responsibility to a manual process and can leave known fixes unapplied. Use one only when compatibility or availability policy requires it, then name the owner, test path, patch deadline, and monitoring that prevents indefinite delay.

How should unattended upgrades roll through a fleet?

Start with one representative canary, verify package logs, reboot behavior and an external application transaction, then patch bounded batches while healthy nodes remain available. Stop the rollout when acceptance fails. Local timers alone do not provide orchestration, traffic draining, or fleet-wide rollback.

Keep a Patch Receipt

Retain seven fields for every policy change or rehearsal: distribution and version, effective origins, timer next-run window, service-restart policy, reboot owner, rollback evidence, and external acceptance result. Review the record after release upgrades, image changes, repository additions, application migrations, or ownership changes.

The finish line is not “APT ran.” It is a server that selected the intended updates, restarted only under an owned policy, returned through a tested recovery path, and passed the workload check that users actually depend on.

Leave a Reply

Your email address will not be published. Required fields are marked *