Two bar charts of seconds from the first failed request to the first 200 after a reboot, median of 5, measured 3 November 2026 on Ubuntu 24.04. Server under $20: nginx 5.7, Node 8.8, Node with PostgreSQL 8.9. Server with its own kernel: nginx 14.3, Node 17.9, Node with PostgreSQL 17.9.

On a clean Ubuntu 24.04 server, security updates already install themselves. What's off by default is the reboot, so a patched kernel or C library can sit on disk unused for months. And nothing tells you any of this happened.

This guide checks what's on, sets a reboot window, adds a notice to your phone or chat, and shows how long a reboot actually takes you offline. We ran it on clean Ubuntu 24.04.5 servers (2 vCPU, 4 GB) on 3 November 2026, with another machine sending a request every 0.1 seconds. In 5 timed reboots, nginx was back in 5 to 19 seconds and a Node app with PostgreSQL in 8 to 22, depending on the server type.

Step 1: see what's already on

bash
cat /etc/apt/apt.conf.d/20auto-upgrades
systemctl is-enabled unattended-upgrades apt-daily.timer apt-daily-upgrade.timer
systemctl list-timers 'apt-daily*' --no-pager
apt list --upgradable 2>/dev/null | wc -l

On our clean server that was Update-Package-Lists "1" and Unattended-Upgrade "1", all three units enabled, and 18 packages waiting because the image was a few weeks old. So you usually don't install anything. Here's what the stock config does and doesn't do:

SettingUbuntu defaultWhat it means
Sources allowednoble, noble-security, plus the two Ubuntu Pro ESM sourcesSecurity fixes only: 10 of our 18 waiting packages qualified
Install time06:00 plus a random delay of up to 60 minutesCould be your busy hour
Automatic-RebootOffKernel and C library fixes don't take effect until someone reboots
NoticeLog file only/var/log/unattended-upgrades/
Old kernels removedLine commented out in the fileSet it on purpose so /boot doesn't fill

Step 2: set your own window

Don't edit 50unattended-upgrades, because package updates may prompt about it. Put your settings in a later file. This installs at 02:30 and reboots at 03:30, only if a reboot is needed. Times are in the server's time zone, which is UTC unless you changed it.

bash
cat > /etc/apt/apt.conf.d/20auto-upgrades <<EOF
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
EOF

cat > /etc/apt/apt.conf.d/52unattended-upgrades-local <<EOF
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:30";
Unattended-Upgrade::SyslogEnable "true";
EOF

mkdir -p /etc/systemd/system/apt-daily-upgrade.timer.d
cat > /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf <<EOF
[Timer]
OnCalendar=
OnCalendar=*-*-* 02:30
RandomizedDelaySec=15m
EOF

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

apt-config dump | grep -E 'Automatic-Reboot|Periodic::Unattended'     # check it was read
systemctl list-timers apt-daily-upgrade.timer --no-pager              # next run around 02:30

The empty OnCalendar= matters: without it systemd adds your time to the default instead of replacing it. If you run more than one server behind a load balancer, give each a different hour so they don't all go down together.

Step 3: a notice that reaches you

Unattended-Upgrade::Mail works if the server can send mail, which a new one usually can't. A webhook is simpler and works with Slack, Discord, ntfy or your own endpoint. Hook it to the service so it runs after every daily run:

bash
cat > /usr/local/bin/update-notice <<"EOF"
#!/bin/sh
# Posts what the last unattended run did. Runs after apt-daily-upgrade.service.
URL="https://example.com/your-webhook"
LOG=/var/log/unattended-upgrades/unattended-upgrades.log
LAST=$(grep -E "Packages that will be upgraded|All upgrades installed|No packages found" "$LOG" | tail -2 | cut -d" " -f3- | tr "\n" " ")
REBOOT=no; [ -f /var/run/reboot-required ] && REBOOT=yes
curl -fsS -m 10 -X POST -d "$(hostname): $LAST reboot_required=$REBOOT" "$URL" >/dev/null
EOF
chmod +x /usr/local/bin/update-notice

mkdir -p /etc/systemd/system/apt-daily-upgrade.service.d
cat > /etc/systemd/system/apt-daily-upgrade.service.d/notice.conf <<EOF
[Service]
ExecStartPost=-/usr/local/bin/update-notice
EOF
systemctl daemon-reload

The - in front of the path means a failed notice won't mark the update run as failed. Check it for real, not with a dry run. Our receiver got this after the run below:

bash
apt-get update
systemctl start apt-daily-upgrade.service
systemctl status apt-daily-upgrade.service --no-pager | head -5
tail -3 /var/log/unattended-upgrades/unattended-upgrades.log
text
ubuntu: INFO Packages that will be upgraded: distro-info-data libbrotli1 INFO All upgrades installed  reboot_required=no
RunOwn-kernel serverServer under $20
unattended-upgrade --dry-run26 sNot timed
Real run through the service, first time39 s, 10 packages38 to 42 s

Two things that bit us

systemctl start can return early. Ours printed Warning! D-Bus connection terminated and came back while the upgrade was still running, because the update restarted D-Bus. Check that the service has gone inactive before you trust the result.

Your deploy script can hit the lock. While that was running, our next command failed with E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 1862 (unattended-upgr). If a script installs packages near your update window, make it wait:

bash
apt-get -o DPkg::Lock::Timeout=300 install -y yourpackage

Step 4: check what still needs a restart

Installing a fixed library doesn't help a process that still has the old one in memory. needrestart tells you which ones. It's on Ubuntu's server images already; add it if it's missing.

text
apt-get install -y needrestart
needrestart -b
# NEEDRESTART-KSTA: 1                     1 means the running kernel is current
# NEEDRESTART-SVC: dbus.service           still using an old library
# NEEDRESTART-SVC: systemd-logind.service
ls /var/run/reboot-required*              # exists only when a reboot is wanted

In our run the kernel didn't change, so no reboot was flagged, yet four services were still on old libraries. That's the gap the reboot window closes when a reboot is flagged, and the reason for a weekly look at this list.

Step 5: make sure your apps come back

A reboot is only cheap if everything restarts by itself. For systemd, that means an [Install] section and enable; for Docker, a restart policy. Order your app after its database, or its first requests will fail:

ini
[Unit]
After=network.target postgresql.service

[Service]
ExecStart=/usr/bin/node /srv/app/app.js
Restart=always
RestartSec=1

[Install]
WantedBy=multi-user.target

How long you're down

We ran nginx on port 80, a Node 18 app on port 3000, and the same app answering select 1 from PostgreSQL. Downtime here runs from the first failed request to the first 200. Each server was rebooted 5 times with systemctl reboot. These were small apps with nothing to warm up, so add your own start time.

StackServer under $20: median (range)Own-kernel server: median (range)
nginx5.7 s (4.9 to 8.5)14.3 s (13.8 to 18.7)
Node8.8 s (7.9 to 11.7)17.9 s (17.1 to 22.1)
Node + PostgreSQL8.9 s (8.2 to 11.7)17.9 s (17.1 to 22.1)

Node came back about 3 seconds after nginx on both because it waited for PostgreSQL, which systemd-analyze blame put at 2.8 seconds and the slowest unit of the boot. The database itself added no further wait for select 1. The own-kernel figures come from a virtual machine in our lab, and real servers with slower firmware or disks can take longer, so time your own once before you choose a window.

Prove the automatic reboot works

Don't wait for a kernel update to find out. This fakes the flag and sets the time to now. It will reboot the server.

bash
touch /var/run/reboot-required
echo 'Unattended-Upgrade::Automatic-Reboot-Time "now";' > /etc/apt/apt.conf.d/53test
unattended-upgrade -v
# No packages found that can be upgraded unattended and no pending auto-removals
# Found /var/run/reboot-required, rebooting

# after it comes back:
rm /etc/apt/apt.conf.d/53test
uptime -p
tail -2 /var/log/unattended-upgrades/unattended-upgrades.log

That reboot gave the same downtime as the others: 6.1 seconds for nginx and 10.1 for Node on a server under $20.

What our smaller servers can't do

Step$20 a month and upUnder $20
Security updates, windows, noticesYesYes, tested on 2 vCPU, 4 GB
Automatic rebootYesYes, tested
Kernel updatesYes, yours to install and reboot forNot applicable: no kernel package is installed, and we look after the kernel
needrestartPreinstalledNot preinstalled, add it

Next on a new server: lock down SSH, ufw and fail2ban, and if you run containers, see Docker's firewall and log settings.

Sources

Our own runs on Ubuntu 24.04.5, 3 November 2026: unattended-upgrades 2.9.1, needrestart 3.6, nginx 1.24, Node 18.19, PostgreSQL 16.15; one server type under QEMU/KVM with kernel 6.8.0-147, one on a real server under $20 with the same 2 vCPU and 4 GB. Docs: Ubuntu Server, automatic updates, Debian wiki, UnattendedUpgrades, needrestart.

Pick a region and a budget and see your best servers, all on Ubuntu 24.04 with security updates ready to go.

See the servers