Step-by-step tutorial, 2026
Unattended upgrades on Ubuntu. And proof they really run.
Our own server said “update daily” in its config. It had never installed a single update by itself. Here’s how we found out, and how to check yours in a minute.
What the config said
APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1";
Daily. Looks fine.
What could run
apt-daily.timer masked apt-daily-upgrade.timer masked
Linked to /dev/null. Never starts.
After we unmasked them
01:15 Starting unattended upgrades script next 06:38 UTC
Running every day.
The short answer
Ubuntu installs security updates by itself through the unattended-upgrades package, once a day. Check it with systemctl is-enabled apt-daily.timer apt-daily-upgrade.timer (both should say enabled) and look for a recent run in /var/log/unattended-upgrades/unattended-upgrades.log.
If a timer says masked, nothing installs, whatever the config files say. Fix it with sudo systemctl unmask and enable --now on both. Our free checker below tests all of it from one paste.
Every guide to automatic updates on Ubuntu says the same thing: install unattended-upgrades, set two values to “1”, done.
Our server had both values set to “1”. It had never run. No log file, no updates, nothing but the ones we’d installed by hand.
The reason took one command to find. The server’s image had masked the two systemd timers that start the updates. A masked unit can’t start at all, so the config files were describing a schedule nobody ran.
This tutorial sets automatic updates up properly, and then proves they work. That second part is the one most guides skip.
In this guide you’ll learn
- A free checker: one read-only command, one paste, a clear answer
- The masked-timer trap we found on our own server
- The setup, step by step, with a dry run before the real one
- What it installs by default, and how to add Docker or other repos
- Reboots and restarts: when they happen, and when they can’t help
- How to tell it ran, every day, without logging in to look
Chapter 1
The short answer
Automatic updates need three things to be true at once. Most guides check only the first two.
| Check | Where | Good answer |
|---|---|---|
| Installed | dpkg -s unattended-upgrades | Status: install ok installed |
| Scheduled | /etc/apt/apt.conf.d/20auto-upgrades | Both values "1" |
| Able to run | systemctl is-enabled apt-daily.timer apt-daily-upgrade.timer | enabled, enabled |
| Has run | /var/log/unattended-upgrades/unattended-upgrades.log | A run from today or yesterday |
On a standard Ubuntu server, the package is installed by default and security updates are switched on. Custom and minimal images are where it goes wrong.
Setting up a new server? Deploy a Lite 2, run the checker below in its first minute, and you know your updates are real from day one. It bills by the hour, so testing this costs cents.
Chapter 2
Check your server in one paste
The block below only reads. It prints one labelled line per check. Paste the result and the checker tells you what works, what doesn’t, and the exact fix.
echo "installed $(dpkg -s unattended-upgrades >/dev/null 2>&1 && echo yes || echo no)"
for t in apt-daily.timer apt-daily-upgrade.timer; do echo "timer $t $(systemctl is-enabled $t 2>&1)"; done
apt-config dump APT::Periodic | sed 's/^/periodic /'
echo "kernels $(ls /boot 2>/dev/null | grep -c '^vmlinuz')"
echo "reboot $([ -f /var/run/reboot-required ] && echo yes || echo no)"
echo "lastrun $(sudo grep -h 'Starting unattended upgrades script' /var/log/unattended-upgrades/unattended-upgrades.log 2>/dev/null | tail -n 1 | cut -c1-10)"
echo "today $(date -u +%F)"Read-only: it changes nothing. It asks for sudo once, to read the update log.
Run the block, then paste its output on the left.
Click “Load our server before the fix” to see the real output that started this tutorial: everything looks switched on, and nothing has ever run.
Chapter 3
What we found on our own server
We run the same checks on our own Ubuntu 22.04 server that we’re asking you to run. Here’s what they showed on 24 October 2026.
| Before | After | |
|---|---|---|
| 20auto-upgrades | Both "1" | Both "1" |
| apt-daily.timer | masked | enabled |
| apt-daily-upgrade.timer | masked | enabled |
| Update log | Did not exist | First run 01:15 UTC |
| Updates installed by itself | None, ever | Daily from then on |
Both timers were links to /dev/null, created when the image was built. That’s what masking is: the unit is linked to /dev/null so it can’t be started, even by hand. It shows up on other images too.
We also found an empty /boot. That server starts its kernel from outside, so apt has no kernel to update. Kernel fixes on a server like that come from the platform, not from you.
Chapter 4
Set it up, step by step
Works on Ubuntu 22.04, 24.04 and later. Each step is safe to repeat.
Install the package
sudo apt update sudo apt install unattended-upgrades
Switch on the schedule
sudo dpkg-reconfigure -plow unattended-upgrades
Answer Yes. Then check
cat /etc/apt/apt.conf.d/20auto-upgradesshows both values as “1” (every day).Make sure the timers can run
sudo systemctl unmask apt-daily.timer apt-daily-upgrade.timer sudo systemctl enable --now apt-daily.timer apt-daily-upgrade.timer systemctl list-timers 'apt-daily*'
The last command should list both timers with a time under NEXT. If they weren’t masked, the first line does nothing.
Dry run
sudo unattended-upgrade -v --dry-run
Shows which sources it allows and what it would install, without installing anything.
First real run
sudo unattended-upgrade -v
This creates the log. Ours said “No packages found that can be upgraded unattended”, which was correct: we were already up to date on security fixes.
On our 22.04 server the timers fire at 06:00 and 18:00 for the download and 06:00 for the install, each with a random delay (up to 12 hours and 60 minutes), so thousands of servers don’t hit the mirrors at once. A missed run catches up after boot.
Chapter 5
What it installs, and what it doesn’t
The rules live in the file 50unattended-upgrades, in /etc/apt/apt.conf.d/, under Allowed-Origins.
- By default: security updates. The
-updates,-proposedand-backportslines are commented out. - Our proof: nano and tmux had newer versions waiting in
jammy-updates. The run skipped both, because neither was a security fix. - Other repositories are ignored. Adding a repo does not make unattended-upgrades consider it. You have to list it.
Adding Docker’s repository
Docker’s repository identifies itself as Origin: Docker, with the Ubuntu codename as the suite, so the line is:
Unattended-Upgrade::Allowed-Origins {
// ...the existing lines...
"Docker:${distro_codename}";
};Holding a package back
Unattended-Upgrade::Package-Blacklist {
"postgresql-16$";
};Entries are regular expressions, so end the name with $ to match only that package. Holding back one package can also block updates that depend on it.
Chapter 6
Reboots and restarts
Some updates only take effect after a restart. Ubuntu flags it with the file /var/run/reboot-required.
- See if one is waitingHow to check:
cat /var/run/reboot-required.pkgslists the packages asking for it. - Automatic rebootsHow to check: Off by default. To allow them at a quiet hour, set
Unattended-Upgrade::Automatic-Reboot "true";andUnattended-Upgrade::Automatic-Reboot-Time "04:00";in 50unattended-upgrades. Without a time, the default is “now”. - Logged-in usersHow to check:
Automatic-Reboot-WithUsersdefaults to true, so it reboots even while someone is logged in. Set it to false if that would hurt. - Services, not the whole serverHow to check: From Ubuntu 24.04,
needrestartrestarts affected services automatically. On 22.04, runsudo needrestart -r lafter updates to list services still running old code.
Chapter 7
How to tell it ran
Once a week, or from a monitoring script, look at the evidence instead of the config.
| Look at | What it tells you |
|---|---|
| /var/log/unattended-upgrades/unattended-upgrades.log | What it decided: allowed sources, packages installed or skipped |
| /var/log/unattended-upgrades/unattended-upgrades-dpkg.log | The raw install output, including errors |
| journalctl -u apt-daily-upgrade.service | Whether the timer actually started it |
| systemctl list-timers 'apt-daily*' | When it last ran and will run next |
Want an email instead? Set Unattended-Upgrade::Mail to your address. It needs a working mail client on the server, which most VPSes don’t have out of the box.
MythIf 20auto-upgrades says "1", updates are on.
Only if both timers can run. Ours said “1” and never ran.
MythAutomatic updates will break my server.
The default installs security fixes only, from Ubuntu’s own archive. The risky parts, other repos and automatic reboots, are off until you turn them on.
MythIt patches the kernel too, so I never need to reboot.
A new kernel package needs a reboot to load. And if the platform supplies the kernel, apt never touches it.
Now it’s your move
Automatic updates are one of the cheapest security wins on a server, as long as they actually run. Check the timers, check the log, and run the checker again after any rebuild.
Next, lock down logins with our SSH hardening tutorial, or see the full VPS security guide.
Want a server that patches itself from day one? Deploy a Lite, run the setup above in five minutes, and let it look after itself. Billed by the hour, never more than the monthly price.