Unattended-Upgrades Configuration (VM & LXC)#

The objective of this guide is to safely automate software updates across a high-density environment of virtual machines and LXC containers, significantly reducing daily manual maintenance without compromising infrastructure stability. By shifting from a manual execution model to a structured deployment using the native unattended-upgrades utility, the system handles the download, installation, and post-update cleanup of all packages automatically in the background. Rather than allowing unpredictable automated reboots that could disrupt active services or interfere with critical backup windows, this configuration enforces a strict “notify-only” baseline. It leverage a lightweight mail client to alert the administrator immediately when a reboot is pending, combining hands-free patching with complete operational control.

This note outlines how to configure Debian/Ubuntu-based Virtual Machines and LXC containers to automatically download and install all software updates, handle automatic cleanup, skip automatic reboots, and flag when a manual reboot is required.


1 Package Installation#

To prevent the automatic installation of a heavy mail transfer agent (MTA) like Postfix or Exim4, install msmtp and msmtp-mta in the same execution block as unattended-upgrades and mailutils:

sudo apt update && sudo apt install unattended-upgrades msmtp msmtp-mta mailutils -y

2 Email Configuration (msmtp)#

2.1 Create Configuration File#

Create the system-wide MSMTP configuration file:

sudo nano /etc/msmtprc

Populate the file with your SMTP relay settings:

defaults
auth             on
tls              on
tls_trust_file   /etc/ssl/certs/ca-certificates.crt
logfile          /var/log/msmtp.log

# Force the envelope-from and From: header to match your authenticated user
set_from_header     on
allow_from_override off

account          default
host             smtp.gmail.com
port             587
from             proxmox.app@gmail.com
user             proxmox.app@gmail.com
passwordeval     op read "op://Private/unattended-upgrades-email/UNATTENDED_UPGRADES_EMAIL_HOST_PASSWORD"

Using passwordeval with op read requires that the 1Password CLI (op) is installed on the host and authenticated (e.g. via a 1Password Service Account token). See the [[1Password CLI]] note for setup details.

2.2 Secure Config Permissions#

Because /etc/msmtprc contains plain-text passwords, restrict its permissions so it is only readable by root:

sudo chmod 600 /etc/msmtprc
sudo chown root:root /etc/msmtprc

3 Configure the Upgrade Logic#

Open the main configuration file:

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

Replace or modify the relevant sections to match the configurations below:

3.1 Target Secure Origins Only (Safe Mode)#

Find the Unattended-Upgrade::Allowed-Origins or Unattended-Upgrade::Origins-Pattern block. Never use a wildcard (origin=*) across 25+ nodes; instead, limit updates to stable security origins and whitelist specific PPAs if necessary:

Unattended-Upgrade::Origins-Pattern {
        "origin=Debian,a=stable";
        "origin=Debian,a=stable-security";
        // "origin=Ubuntu,a=lunar-proposed"; // Uncomment only if needed for HW support (e.g. GPU)
};

Explicitly exclude the kernel. Add *-kernel* to the Unattended-Upgrade::Package-blacklist block. Kernel updates should always be manual in a homelab to prevent widespread instability from driver breakage.

Stagger your update schedule#

With 25+ nodes, do not run all systems on the same timer. Distribute them across at least four time bins over a 5-day cycle (e.g., Batches A–D):

  • Batch A (Critical ~5 VMs): Runs Monday only.
  • Batches B, C, D: Run Tuesday/Thursday/Friday respectively.

A bad package rollout on Batch A gives you the entire week before it hits your other 20 systems.

3.2 Configure E-mail Alerts for Reboots#

Set up notifications for when packages are updated or a system reboot is triggered:

Unattended-Upgrade::Mail "proxmox.app@gmail.com";
Unattended-Upgrade::MailReport "on-change";

3.3 Enforce No Reboots & Enable Autoremove#

Ensure the system never reboots on its own and cleans up orphaned packages/kernels automatically:

Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";

4 Activate the Automation Timer#

Define how frequently the background tasks should run by modifying the periodic configuration file:

sudo nano /etc/apt/apt.conf.d/20auto-upgrades

Overwrite the file contents with the following settings:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
  • “1”: Runs the task once a day.
  • AutocleanInterval “7”: Clears out the local package cache every 7 days.

5 Adjusting the Execution Time (Optional)#

By default, systemd runs the APT daily timers at randomized times to prevent overloading package mirror servers. To schedule the upgrades for a specific time (e.g. daily at 8:20 AM), use systemd overrides.

5.1 Schedule Package Upgrades (8:20 AM)#

Set apt-daily-upgrade.timer to trigger unattended-upgrades at your exact target time:

sudo systemctl edit apt-daily-upgrade.timer

Add the following config:

[Timer]
OnCalendar=
OnCalendar=*-*-* 08:20:00
RandomizedDelaySec=0
Persistent=true

5.2 Apply Changes#

Reload the systemd manager and restart both timers to apply the new schedule:

sudo systemctl daemon-reload
sudo systemctl restart apt-daily.timer apt-daily-upgrade.timer

6 Enable and Start Services#

On minimal Debian/Ubuntu installations or LXC containers, the unattended-upgrades service and daily timers might not be enabled by default.

sudo systemctl enable --now unattended-upgrades
# Verify the daily timers are scheduled
sudo systemctl status apt-daily.timer apt-daily-upgrade.timer

7 Verification & Testing#

Verify that both local mail routing and the upgrade configuration are functional.

7.1 Test Mail Routing#

Send a test email using the system mail command to verify that msmtp successfully relays it:

echo "Test email body from $(hostname)" | mail -s "Test Email" proxmox.app@gmail.com

If it fails, inspect the MSMTP log:

sudo cat /var/log/msmtp.log

7.2 Test Unattended-Upgrades (Dry Run)#

Simulate the unattended upgrade process to verify origin-matching and configuration rules:

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

Inspect the logs to confirm execution:

sudo cat /var/log/unattended-upgrades/unattended-upgrades.log