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 -y2 Email Configuration (msmtp)#
2.1 Create Configuration File#
Create the system-wide MSMTP configuration file:
sudo nano /etc/msmtprcPopulate 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
passwordevalwithop readrequires 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/msmtprc3 Configure the Upgrade Logic#
Open the main configuration file:
sudo nano /etc/apt/apt.conf.d/50unattended-upgradesReplace 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 theUnattended-Upgrade::Package-blacklistblock. 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-upgradesOverwrite 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.timerAdd the following config:
[Timer]
OnCalendar=
OnCalendar=*-*-* 08:20:00
RandomizedDelaySec=0
Persistent=true5.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.timer6 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.timer7 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.comIf it fails, inspect the MSMTP log:
sudo cat /var/log/msmtp.log7.2 Test Unattended-Upgrades (Dry Run)#
Simulate the unattended upgrade process to verify origin-matching and configuration rules:
sudo unattended-upgrades --dry-run --debugInspect the logs to confirm execution:
sudo cat /var/log/unattended-upgrades/unattended-upgrades.log