> ## Documentation Index
> Fetch the complete documentation index at: https://blog.onetsolutions.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Automatic security updates on a Linux VPS: setup guide

> Turn on automatic security updates on your Linux VPS: unattended-upgrades, dnf-automatic, scheduled reboots and email reports. An OnetSolutions guide.

*Published on November 13, 2026*

Turning on automatic security updates on a Linux VPS takes ten minutes. Not doing it costs days of exposure.

The latest advisories on this blog make the point. With [php2shell](https://blog.onetsolutions.net/en/php2shell-cve-2026-109618.md), attacks started the same morning the fixes shipped. With the [Apache HTTP/2 flaw](https://blog.onetsolutions.net/en/apache-http2-cve-2026-119618.md), the fix exists: what matters is when it lands on your server. With [Copy Fail](https://blog.onetsolutions.net/en/copy-fail-cve-2026-31431.md), the patched kernel only protects you after a reboot.

The risk is not the vulnerability. It is the gap between a fix being published and a fix being applied. This guide shrinks that gap to hours, on Debian, Ubuntu, AlmaLinux and Rocky Linux.

## Which tool for which distribution

Package versions in the official repositories, checked on November 13, 2026:

| Distribution | Tool | Package version |
| - | - | - |
| Debian 12 (Bookworm) | `unattended-upgrades` | 2.9.1+nmu3 |
| Debian 13 (Trixie) | `unattended-upgrades` | 2.12 |
| Ubuntu 22.04 LTS | `unattended-upgrades` | 2.8ubuntu1 |
| Ubuntu 24.04 LTS | `unattended-upgrades` | 2.9.1+nmu4ubuntu1 |
| Ubuntu 26.04 LTS | `unattended-upgrades` | 2.12ubuntu9 |
| AlmaLinux 9, Rocky Linux 9 | `dnf-automatic` (dnf 4) | 4.14.0 |
| AlmaLinux 10, Rocky Linux 10 | `dnf-automatic` (dnf 4) | 4.20.0 |

AlmaLinux 10 and Rocky Linux 10 still ship dnf 4, so the configuration below applies to both generations.

| | `unattended-upgrades` (Debian, Ubuntu) | `dnf-automatic` (AlmaLinux, Rocky) |
| - | - | - |
| Security only | `Origins-Pattern` / `Allowed-Origins` list | `upgrade_type = security` |
| Reboot | `Automatic-Reboot`, when `/var/run/reboot-required` exists | `reboot = when-needed` or `when-changed` |
| Reboot time | `Automatic-Reboot-Time` | Timer schedule + `reboot_command` delay |
| Email report | `Mail`, `MailReport` | `emit_via = email`, `[email]` section |
| Simulation | `unattended-upgrade --dry-run` | `dnf upgrade --security --assumeno` |
| systemd timer | `apt-daily.timer`, `apt-daily-upgrade.timer` | `dnf-automatic.timer` |

## Before enabling reboots: take a snapshot

An automatic reboot is a reboot nobody is watching. Take a [snapshot](https://help.onetsolutions.net/vps/snapshot) from the console before turning it on. If the first reboot goes wrong, you are one click away from a known-good state.

[Backups](https://help.onetsolutions.net/vps/backup) remain your baseline protection: a snapshot is a restore point, not a backup.

## Set up unattended-upgrades on Debian and Ubuntu

### Install and enable

Ubuntu installs `unattended-upgrades` by default. On Debian, install it. Running the command on either is harmless:

```bash theme={null}
sudo apt update
sudo apt install unattended-upgrades
```

Enabling it lives in `/etc/apt/apt.conf.d/20auto-upgrades`. Each value is a number of days: `1` for daily, `0` to disable.

```bash theme={null}
sudo tee /etc/apt/apt.conf.d/20auto-upgrades >/dev/null <<'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOF
```

### Restrict it to security updates

That is already the default, with one nuance per distribution.

* **Ubuntu**: `Allowed-Origins` covers the base release, the `-security` pocket and the ESM repositories when present. `-updates`, `-proposed` and `-backports` are commented out.
* **Debian**: `Origins-Pattern` covers `Debian-Security` and your release's own archive (the one that receives stable point releases). Backports and `-updates` are commented out.

Leave `50unattended-upgrades` alone: the Debian wiki recommends putting your settings in a separate file, `52unattended-upgrades-local`, which is read after it.

On Debian, if you want security advisories strictly, clear the list and declare it again. An APT list cannot be overridden, only cleared with `#clear`:

```bash theme={null}
sudo tee /etc/apt/apt.conf.d/52unattended-upgrades-local >/dev/null <<'EOF'
// Debian: security archive only
#clear Unattended-Upgrade::Origins-Pattern;
Unattended-Upgrade::Origins-Pattern {
        "origin=Debian,codename=${distro_codename},label=Debian-Security";
        "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
};
EOF
```

> [!NOTE]
>
> The file Ubuntu ships says it plainly: a security fix can depend on a package from the main archive. That is why the main archive is in the default lists. With the strict filter, such a fix waits: the simulation below shows it.

To see the origins actually configured on your server:

```bash theme={null}
apt-cache policy | grep release
```

### Decide how reboots happen

A patched kernel, C library or systemd only protects you after a reboot. When a package needs one, it creates `/var/run/reboot-required`. `unattended-upgrades` reboots only if that file exists and you have allowed it.

| Situation | Setting |
| - | - |
| Single VPS, a few minutes of downtime at night is fine | `Automatic-Reboot "true"` and `Automatic-Reboot-Time "03:30"` |
| You want to pick the moment yourself | `Automatic-Reboot "false"` plus an email report |
| Several VPS serving the same service | Automatic reboot, at a different time on each server |

Append the settings to the local file (it is created if it does not exist yet):

```bash theme={null}
sudo tee -a /etc/apt/apt.conf.d/52unattended-upgrades-local >/dev/null <<'EOF'
// Reboot only when /var/run/reboot-required exists
Unattended-Upgrade::Automatic-Reboot "true";
// Reboot even if someone is logged in over SSH
Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
// Local reboot time, passed to shutdown
Unattended-Upgrade::Automatic-Reboot-Time "03:30";
EOF
```

`Automatic-Reboot-Time` also accepts `now` (the default) or `+m`, a delay in minutes.

The upgrade itself is triggered by `apt-daily-upgrade.timer`, set by default to 6:00 with a random delay of up to an hour. To have it run before that night's reboot, move it earlier:

```bash theme={null}
sudo systemctl edit apt-daily-upgrade.timer
```

```ini theme={null}
[Timer]
# The empty line clears the original schedule
OnCalendar=
OnCalendar=*-*-* 02:00
```

```bash theme={null}
sudo systemctl restart apt-daily-upgrade.timer
```

Keep the random delay: the Debian wiki recommends it so that every server does not hit the mirrors in the same second.

### Get an email report

```bash theme={null}
sudo tee -a /etc/apt/apt.conf.d/52unattended-upgrades-local >/dev/null <<'EOF'
Unattended-Upgrade::Mail "admin@example.com";
// always, only-on-error, on-change (default)
Unattended-Upgrade::MailReport "on-change";
EOF
```

Reports go through the `mail` command: you need a package that provides it, such as `bsd-mailx`, and a local mail server that can deliver outside the VPS.

```bash theme={null}
sudo apt install bsd-mailx
echo "test" | mail -s "Test from $(hostname)" admin@example.com
```

If this test does not arrive, neither will the report. Our guide to [SPF, DKIM, DMARC and reverse DNS](https://blog.onetsolutions.net/en/spf-dkim-dmarc-reverse-dns-vps-mail-server.md) covers deliverability.

### Check it: simulation, timers and logs

```bash theme={null}
# Full simulation, nothing gets installed
sudo unattended-upgrade -v --dry-run

# Next runs of both timers
systemctl list-timers apt-daily.timer apt-daily-upgrade.timer

# What happened during recent runs
sudo journalctl -u apt-daily-upgrade.service --since "7 days ago"
sudo tail -n 50 /var/log/unattended-upgrades/unattended-upgrades.log

# Is a reboot pending?
test -f /var/run/reboot-required && echo "Reboot required" || echo "No reboot required"
```

**Good result:** the simulation lists pending security packages without errors, and `list-timers` shows a next run within 24 hours.

## Configure dnf-automatic on AlmaLinux and Rocky Linux

### Install

```bash theme={null}
sudo dnf install dnf-automatic
```

### Security only, reboots and email

Everything is set in `/etc/dnf/automatic.conf`. Change these lines in their respective sections:

```ini theme={null}
[commands]
# Only updates that come with a security advisory
upgrade_type = security
download_updates = yes
apply_updates = yes
# never (default), when-changed (after any update)
# or when-needed (only when required: kernel, systemd...)
reboot = when-needed
# Default: reboot 5 minutes after the update
reboot_command = "shutdown -r +5 'Rebooting after applying package updates'"

[emitters]
emit_via = stdio,email

[email]
email_from = root@vps.example.com
email_to = admin@example.com
email_host = localhost
```

With `stdio`, the report also goes to the systemd journal. With `email`, `dnf-automatic` hands the message to the SMTP server named in `email_host` itself: a local mail server, such as Postfix, must accept it.

### Pick the time and enable it

The reboot follows the upgrade by five minutes. Picking the reboot time therefore means picking the timer's schedule, set by default to 6:00 with a random delay of up to an hour:

```bash theme={null}
sudo systemctl edit dnf-automatic.timer
```

```ini theme={null}
[Timer]
OnCalendar=
OnCalendar=*-*-* 03:00
```

```bash theme={null}
sudo systemctl enable --now dnf-automatic.timer
```

The package also ships `dnf-automatic-install.timer`, which installs regardless of `apply_updates`, and `dnf-automatic-notifyonly.timer`, which only notifies. Enable just one.

### Check it

```bash theme={null}
# Pending security advisories
sudo dnf updateinfo list --security

# Simulation: shows the transaction, then answers no
sudo dnf upgrade --security --assumeno

# Next timer run
systemctl list-timers 'dnf-automatic*'

# Reports from recent runs
sudo journalctl -u dnf-automatic.service --since "7 days ago"

# Exit code 1: a reboot is needed (provided by dnf-plugins-core)
sudo dnf needs-restarting -r
```

> [!WARNING]
>
> `sudo systemctl start dnf-automatic.service` performs a real run: with the configuration above, it installs the fixes and may reboot the VPS five minutes later. Use the simulation to test.

## If the VPS does not come back

It is rare, and everything can be recovered from the console.

| Situation | Action |
| - | - |
| The VPS no longer answers on SSH after the reboot | Open the [KVM console](https://help.onetsolutions.net/vps/kvm-console): you see where the boot stops |
| The new kernel does not boot | From the KVM console, pick the previous kernel in the GRUB menu |
| A filesystem or configuration needs repairing | Boot into [rescue mode](https://help.onetsolutions.net/vps/rescue-mode): our [rescue mode guide](https://blog.onetsolutions.net/en/vps-rescue-mode-repair-server-that-wont-boot.md) walks through the repair |
| You simply want to roll back | Restore the [snapshot](https://help.onetsolutions.net/vps/snapshot) taken before enabling reboots |

## Common pitfalls

**Services still run the old code.** An upgraded package does not replace the process in memory. On Ubuntu 24.04 and later, `unattended-upgrades` calls `needrestart`, which restarts the affected services. Elsewhere, list processes using replaced files with `sudo dnf needs-restarting` on AlmaLinux and Rocky, and restart the services concerned. Our [nginx audit routine](https://blog.onetsolutions.net/en/audit-and-harden-nginx-on-a-vps.md) shows how to spot a process older than its update.

**Third-party repositories are not covered.** The default origins only target the distribution's own repositories. PHP from an external repository, Docker, nginx.org: their fixes do not go through this mechanism. Add their origin to `Origins-Pattern` after reading it from `apt-cache policy`, or follow their advisories yourself.

**A held package stays held.** A package marked with `apt-mark hold`, or listed in `Package-Blacklist`, is never upgraded automatically. Check with `apt-mark showhold`.

**A manual upgrade waits its turn.** While `unattended-upgrades` is working, it holds the `dpkg` lock. An `apt upgrade` started at the same time waits: do not delete the lock.

**Containers do not follow the host.** Docker images carry their own packages. They are updated by rebuilding or pulling a new image, not by the host.

**The report never arrives.** No `mail` command, no local mail server, or a message rejected by the recipient: send a test email before relying on reports.

## Going further

* [Snapshots](https://help.onetsolutions.net/vps/snapshot): create a restore point before changing the system
* [Backups](https://help.onetsolutions.net/vps/backup): baseline protection for your data
* [KVM console](https://help.onetsolutions.net/vps/kvm-console) and [rescue mode](https://help.onetsolutions.net/vps/rescue-mode): regain control of a VPS that stopped responding
* [SSH keys](https://help.onetsolutions.net/vps/ssh-keys): the other security setting to make on day one
* [Operating systems](https://help.onetsolutions.net/vps/operating-system): available versions and their end-of-support dates
* [Enhanced security on a Linux VPS](https://blog.onetsolutions.net/en/enhanced-vps-linux-security.md), for the next layers

Have a question about a server hosted with us? [Our team is available](https://help.onetsolutions.net/console/support) from your client area.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.