Personally, I solved this by creating a small automated update system via cron instead of relying entirely on unattended-upgrades.
This gives me:
- automatic updates;
- logging;
- reboot control;
- automatic cleanup;
- prevention of APT conflicts;
- and the possibility of adding optional integrations.
In my case, I use something similar to this:
1. Create the script
sudo nano /usr/local/sbin/daily-apt-update.sh
2. Make it executable
sudo chmod +x /usr/local/sbin/daily-apt-update.sh
3. Full script
#!/usr/bin/env bash
set -Eeuo pipefail
LOGFILE="/var/log/apt-autoupdate.log"
LOCKFILE="/var/run/apt-autoupdate.lock"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
}
trap 'log "ERROR: Line $LINENO exited with code $?"; echo "--------------------------------------------------" >> "$LOGFILE"' ERR
# Prevent multiple executions
exec 9>"$LOCKFILE"
if ! flock -n 9; then
log "Another execution is already running. Exiting."
exit 0
fi
log "Starting automatic update process"
IONICE="ionice -c3"
NICE="nice -n 10"
export DEBIAN_FRONTEND=noninteractive
# Wait for APT locks
log "Checking APT locks..."
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \
fuser /var/lib/apt/lists/lock >/dev/null 2>&1 || \
fuser /var/cache/apt/archives/lock >/dev/null 2>&1; do
log "APT is locked by another process. Waiting 30 seconds..."
sleep 30
done
log "Running: apt-get update"
$IONICE $NICE /usr/bin/apt-get update >> "$LOGFILE" 2>&1
log "Running: apt-get upgrade"
$IONICE $NICE /usr/bin/apt-get -y \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" \
upgrade >> "$LOGFILE" 2>&1
log "Running: apt-get autoremove"
$IONICE $NICE /usr/bin/apt-get -y autoremove >> "$LOGFILE" 2>&1
log "Running: apt-get clean"
$IONICE $NICE /usr/bin/apt-get clean >> "$LOGFILE" 2>&1
# Optional example for security software updates
# security-software --update
log "Update process completed successfully."
if [ -f /var/run/reboot-required ]; then
log "Reboot required."
if [ -f /var/run/reboot-required.pkgs ]; then
log "Packages requiring reboot:"
while IFS= read -r pkg; do
log " - $pkg"
done < /var/run/reboot-required.pkgs
fi
else
log "No reboot required."
fi
echo "--------------------------------------------------" >> "$LOGFILE"
4. Test manually
sudo /usr/local/sbin/daily-apt-update.sh
5. Check logs
sudo tail -f /var/log/apt-autoupdate.log
6. Configure cron
sudo crontab -e
Then:
# Daily updates at 03:30
30 3 * * * /usr/local/sbin/daily-apt-update.sh
# Automatic reboot at 04:30 only if required
30 4 * * * [ -f /var/run/reboot-required ] && /sbin/reboot
One important thing:
I do NOT recommend rebooting only 10 minutes after the upgrade process starts, because some upgrades can take quite a long time, especially on larger servers.
I also avoid automatically using:
apt full-upgrade
or:
apt dist-upgrade
on production servers.
I prefer sticking with:
apt upgrade
because it tends to be more predictable and safer in hosting environments.
Another important detail in my case:
I intentionally chose low-traffic hours to minimize any possible impact during heavier upgrades or reboots.
Also, since I run multiple servers, not all of them perform updates at the same time.
I stagger update schedules across different nodes to avoid:
- simultaneous reboots;
- I/O spikes;
- excessive infrastructure load;
- or maintenance happening at the same time on multiple critical services.
For example:
- some servers update at 03:30;
- others at 04:00;
- others at 04:30;
- etc.
Especially in environments with:
- web nodes;
- mail nodes;
- DNS servers;
- databases;
- proxies/load balancers;
I think it is very important to avoid having the entire infrastructure entering maintenance mode simultaneously.
7. Log rotation
sudo nano /etc/logrotate.d/apt-autoupdate
Content:
/var/log/apt-autoupdate.log {
weekly
rotate 4
compress
missingok
notifempty
create 0640 root adm
}
This does not completely replace a centralized update management system in the panel itself, but honestly, it has worked very well for me and gives much more control over the entire process.