unattended-upgrades goed configureren — en waarom je reboot-required moet monitoren
De standaardinstallatie van unattended-upgrades patcht security-updates maar laat services op oude libraries draaien en de nieuwe kernel op schijf staan. Dit is de configuratie die dat oplost voor Ubuntu 24.04 en Debian 12: origins, blacklist, needrestart, reboot-beleid, mail — plus het cron-script dat meldt welke hosts al dagen op een reboot wachten.
Inhoud
apt install unattended-upgrades en klaar — dat is wat de meeste servers hebben, en het is beter dan niets. Maar de standaardconfiguratie heeft drie gaten: services blijven na een libssl-update doorwerken met de oude library in het geheugen (dus de CVE is nog steeds actief), een nieuwe kernel wordt geïnstalleerd maar nooit geboot, en niemand krijgt te horen wat er is gebeurd. Dit artikel dicht die drie gaten en zet er een fleet-brede reboot-check naast.
Stap 1: installeren en de basis controleren
sudo apt install -y unattended-upgrades apt-listchanges needrestart
# Zijn de periodieke timers aan?
cat /etc/apt/apt.conf.d/20auto-upgrades
# APT::Periodic::Update-Package-Lists "1";
# APT::Periodic::Unattended-Upgrade "1";
systemctl list-timers 'apt-daily*' --no-legend
Ontbreekt 20auto-upgrades (op Debian-minimal soms): sudo dpkg-reconfigure -plow unattended-upgrades maakt het aan.
Stap 2: de configuratie die je wilt
Alles in /etc/apt/apt.conf.d/52unattended-upgrades-local — een eigen bestand met een hoger nummer overschrijft de defaults uit 50unattended-upgrades zonder dat een package-update je wijzigingen overschrijft.
sudo tee /etc/apt/apt.conf.d/52unattended-upgrades-local >/dev/null <<'EOF'
// Ubuntu: security + ESM. Gewone -updates bewust NIET automatisch (die bevatten
// feature-changes; die plan je). Debian gebruikt Origins-Pattern, zie onder.
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
};
// Wat je NOOIT ongepland wilt: kernel (aparte how-to), Docker, databases.
Unattended-Upgrade::Package-Blacklist {
"linux-image-";
"linux-headers-";
"docker-ce";
"containerd.io";
"postgresql-";
"mysql-server";
"mariadb-server";
};
// Opruimen
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// Reboot: NIET automatisch op productie. Wel melden (stap 4).
Unattended-Upgrade::Automatic-Reboot "false";
// Op dev/staging mag het wel, in een vast venster, en niet als iemand ingelogd is:
// Unattended-Upgrade::Automatic-Reboot "true";
// Unattended-Upgrade::Automatic-Reboot-Time "03:30";
// Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
// Mail bij wijzigingen of fouten (vereist een werkende lokale mailer of msmtp)
Unattended-Upgrade::Mail "ops@example.be";
Unattended-Upgrade::MailReport "on-change";
// Naar syslog/journal, zodat het in je logs zit
Unattended-Upgrade::SyslogEnable "true";
// Fixeer halve installaties in plaats van te stoppen
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::InstallOnShutdown "false";
EOF
Op Debian 12 vervang je het Allowed-Origins-blok door:
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
"origin=Debian,codename=${distro_codename},label=Debian-Security";
};
Test altijd eerst droog:
sudo unattended-upgrade --dry-run --debug 2>&1 | grep -E 'Allowed origins|Packages that will be upgraded|blacklist|No packages'
Stap 3: services herstarten na een library-update (needrestart)
Dit is het gat dat het vaakst open blijft. Na apt upgrade libssl3t64 draait nginx door met de oude libssl in het geheugen tot iemand hem herstart. needrestart detecteert dat en kan het automatisch doen — maar staat standaard in list-modus wanneer er geen terminal is.
sudo tee /etc/needrestart/conf.d/50-auto.conf >/dev/null <<'EOF'
# a = automatisch herstarten, l = alleen tonen, i = vragen (nutteloos zonder tty)
$nrconf{restart} = 'a';
# Services die je NIET automatisch wilt herstarten (regex op unit-naam)
$nrconf{override_rc}{qr(^postgresql)} = 0;
$nrconf{override_rc}{qr(^mariadb)} = 0;
$nrconf{override_rc}{qr(^docker)} = 0;
$nrconf{override_rc}{qr(^ssh)} = 1; # ssh mag wel: bestaande sessies blijven open
EOF
# Wat zou hij nu herstarten?
sudo needrestart -r l
needrestart draait automatisch na elke apt-run via de hook in /etc/apt/apt.conf.d/99needrestart. Met restart = 'a' herstart hij dus alles wat op een verouderde library draait, behalve wat je hebt uitgesloten. Die uitgesloten services (databases) verschijnen in de reboot-check van stap 4 als "needs restart".
Stap 4: reboot-required fleet-breed zichtbaar maken
De nieuwe kernel staat na een update op schijf, en /var/run/reboot-required verschijnt. Niemand ziet dat bestand. Dit script meldt elke ochtend welke hosts wachten, en hoe lang al:
sudo tee /usr/local/sbin/reboot-check.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
NTFY="https://ntfy.example.be/patching"
HOST=$(hostname -s)
MSG=()
if [ -f /var/run/reboot-required ]; then
days=$(( ( $(date +%s) - $(stat -c %Y /var/run/reboot-required) ) / 86400 ))
pkgs=$(sort -u /var/run/reboot-required.pkgs 2>/dev/null | paste -sd, -)
MSG+=("reboot-required since ${days}d (${pkgs:-?})")
fi
# Services die op verouderde libraries draaien (needrestart in list-modus, batch)
stale=$(needrestart -b 2>/dev/null | awk -F': ' '/^NEEDRESTART-SVC/{print $2}' | paste -sd, -)
[ -n "$stale" ] && MSG+=("services on old libs: $stale")
# Laatste geslaagde unattended-upgrade run
last=$(grep -h 'Packages that were upgraded' /var/log/unattended-upgrades/unattended-upgrades.log 2>/dev/null | tail -1 | cut -c1-19)
[ -n "$last" ] && MSG+=("last auto-upgrade: $last")
# Openstaande security-updates die NIET geïnstalleerd zijn (blacklist of fout)
open=$(apt list --upgradable 2>/dev/null | grep -c security)
[ "$open" -gt 0 ] && MSG+=("$open security updates pending")
if [ ${#MSG[@]} -gt 0 ]; then
printf '%s\n' "${MSG[@]}" | curl -s -H "Title: patch@$HOST" -H "Priority: default" --data-binary @- "$NTFY" >/dev/null
fi
EOF
sudo chmod 0755 /usr/local/sbin/reboot-check.sh
echo '30 7 * * * root /usr/local/sbin/reboot-check.sh' | sudo tee /etc/cron.d/reboot-check
Het ntfy-topic patching wordt elke ochtend je to-do-lijst: hosts die drie dagen op een reboot wachten, en databases die na een libssl-update nog niet zijn herstart.
Stap 5: het tijdstip in de hand houden
apt-daily-upgrade.timer vuurt om 06:00 met tot 60 minuten willekeurige vertraging. Op een fleet betekent dat: alle hosts patchen tegelijk, midden in de ochtendpiek van sommige klanten. Verschuif het:
sudo systemctl edit apt-daily-upgrade.timer
[Timer]
OnCalendar=
OnCalendar=*-*-* 03:00
RandomizedDelaySec=45min
Verschillende groepen (web, db, batch) een ander uur geven is de eenvoudigste vorm van staged rollout: als om 03:00 iets breekt op de web-groep, is de db-groep om 04:00 nog niet aan de beurt en kun je ingrijpen.
Valkuilen
-updatesin de origins op Ubuntu. Sommige gidsen zetten${distro_id}:${distro_codename}-updateserbij. Dat installeert ook niet-security-updates met gedragswijzigingen (nieuwe PHP-minor, nieuwe systemd). Doe dat alleen op hosts waar je dat bewust wilt.- Phased updates. Ubuntu rolt sommige updates gefaseerd uit;
apt list --upgradabletoont ze als "deferred". Dat is normaal, geen fout. Wil je ze toch meteen:APT::Get::Always-Include-Phased-Updates "true";— maar dan ben je vrijwillig proefkonijn. - dpkg-lock-conflicten. Ansible, Puppet of een hand-
aptom 03:00 botst op de lock en faalt met "Could not get lock". Plan je config-management buiten het venster van stap 5. - Mail die nergens aankomt.
Mail "ops@example.be"doet niets zonder MTA. Installeermsmtp-mtamet een relay, of laat de mail weg en vertrouw op stap 4. Automatic-Reboot "true"op een host met DKMS-modules. Na de reboot kan een netwerk- of storage-module ontbreken. Zie kernel-updates op productie voordat je automatische reboots aanzet.- Docker-containers zien de host-updates niet. Een gepatchte
libsslop de host doet niets voor delibsslin jepython:3.12-image. Container-images zijn een aparte patchstroom.
Wat je hiermee nog niet hebt
- Overzicht. Dertig ntfy-berichten per ochtend zijn geen dashboard. "Welke hosts wachten langer dan zeven dagen?" vraagt een script over de berichten heen.
- Bewijs van doorlooptijd. Een auditor wil "dagen tussen USN-publicatie en installatie, p95". Dat zit versnipperd in
unattended-upgrades.logendpkg.logper host. - Rollback. Een security-update die een service breekt, wordt om 03:00 geïnstalleerd en om 08:00 ontdekt. unattended-upgrades heeft geen "stop bij fouten op andere hosts"-mechanisme.
Zo doet monsys het
De monsys-agent rapporteert per host de openstaande updates, reboot_required (met sinds wanneer) en de services die op verouderde libraries draaien; de hub toont dat als één fleet-brede lijst en meet de patch-doorlooptijd tegen de publicatiedatum van de USN of DSA. Wie verder wil gaan, zet auto-patch aan: per host én per categorie (OS-packages, app-dependencies) expliciet opt-in met TOTP, uitgevoerd via ondertekende Emergency Action Tokens, met een rollback-gate die de loop op de hele fleet pauzeert zodra één batch fouten geeft. Het "observeert en rapporteert, grijpt nooit zelf in"-principe blijft: geen enkele actie zonder dat een mens hem vooraf heeft toegestaan.
FAQ
Moet ik automatische updates aanzetten op productie?
Security-updates: ja, met de blacklist uit stap 2 voor kernel, databases en Docker. De kans dat een security-update iets breekt is klein; de kans dat een niet-geïnstalleerde security-update wordt misbruikt is groter. Feature-updates (-updates) plan je bewust.
Waarom staat er nog een security-update open terwijl unattended-upgrades draait?
Drie oorzaken, in volgorde van waarschijnlijkheid: het package staat op de blacklist (kernel, database), de update is "phased" en nog niet aan jouw host toegewezen, of de update vereist een pakket uit een origin die je niet hebt toegestaan (bv. -updates). unattended-upgrade --dry-run --debug vertelt welke.
Vervangt needrestart een reboot?
Voor library-updates: ja, het herstart de services die de oude library gebruiken. Voor kernel-, glibc- en systemd-updates: nee, daar blijft een reboot nodig, en dat is precies wat /var/run/reboot-required aangeeft.
Geschreven door het monsys-team — sysadmins die dit dagelijks doen.
Zelf gedaan? Laat monsys het bijhouden.
Alles uit deze how-to draait in monsys als doorlopende check, met historie, alerts en audit-bewijs. 5 servers gratis, EU-gehost in België, geïnstalleerd in 60 seconden.