Automatic updates on openSUSE with os-update and timers

Let the server patch itself on a schedule, reboot when needed, and tell you when something failed.

Part 10 of 10 in openSUSE home server setup. Last updated

In short: Install os-update and enable os-update.timer: it runs daily, picks the right zypper command for the distribution (dup on Tumbleweed) or the one you set in /etc/os-update.conf, restarts services that use old libraries and reboots when needed. Set REBOOT_CMD=none if you want to reboot yourself. Prefer your own schedule? A small systemd service and timer running zypper --non-interactive patch works too. Container images update separately with podman-auto-update.timer. Check the results in the journal.

What to automate

Sensible automatic updates per openSUSE flavour
Leap 16Tumbleweed
System packagesDaily, low risk: fixes onlyDaily or weekly zypper dup, with snapper as safety net
RebootsOnly when a patch needs oneOften: most snapshots bring a new kernel
Container imagesSeparately, per container, with podman auto-update

Why each flavour uses a different zypper command is in the zypper updates guide.

System packages and container images update on separate timers.

os-update: the packaged way

os-update is openSUSE's own small updater, packaged for Tumbleweed and Leap 16. Installing and enabling it needs root:

sudo zypper install os-update
sudo systemctl enable --now os-update.timer
systemctl list-timers os-update.timer      # next run: daily, randomised by up to 2 hours

The defaults live in /usr/share/os-update/os-update.conf. Put only what you change in /etc/os-update.conf:

# /etc/os-update.conf
UPDATE_CMD="auto"          # auto, dup, up or security
REBOOT_CMD="none"          # auto, rebootmgr, reboot or none (just tell me)
RESTART_SERVICES="yes"     # restart services still using old libraries

auto picks the command that fits the distribution, such as dup on Tumbleweed; security installs only security patches. With REBOOT_CMD="auto" it reboots through rebootmgr when that runs (which respects a maintenance window), and with a plain systemctl reboot otherwise.

Is it there and not installed yet? zypper answers without root:

$ rpm -q os-update
package os-update is not installed
$ zypper --no-refresh info os-update | grep -E '^(Name|Version|Summary|Repository) '
Repository     : Main Repository (OSS)
Name           : os-update
Version        : 1.21+git.20260212-1.2
Summary        : Updates the system regularly to stay current and safe

Real output on openSUSE Tumbleweed, 2026-09-28; user and host names and addresses redacted.

A DIY systemd timer

Want your own schedule and nothing else? Two unit files as root do the job on Leap:

# /etc/systemd/system/zypper-patch.service
[Unit]
Description=Install openSUSE patches
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/zypper --non-interactive patch --auto-agree-with-licenses
# /etc/systemd/system/zypper-patch.timer
[Unit]
Description=Weekly openSUSE patches

[Timer]
OnCalendar=Sun *-*-* 04:00:00
RandomizedDelaySec=30min
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now zypper-patch.timer

Persistent=true catches up on a run the server missed while it was off. The cron schedule builder writes other OnCalendar= lines for you. This service does not reboot; combine it with zypper needs-rebooting and a reboot you plan yourself.

Container images

System updates do not touch your container images. For quadlets with AutoUpdate=registry, enable podman's own timer as the container user (no root):

systemctl --user enable --now podman-auto-update.timer
podman auto-update --dry-run               # what would change

Details and the "pin your database images" warning are in the quadlet basics guide.

A throwaway test: a container started as a transient user service with the auto-update label. The dry run lists it with the unit it will restart:

$ systemd-run --user --quiet --unit=demo-auto -E PODMAN_SYSTEMD_UNIT=demo-auto.service podman run --rm --name demo-auto --label io.containers.autoupdate=registry registry.fedoraproject.org/fedora:latest sleep 120
$ podman auto-update --dry-run --format '{{.Unit}} {{.Container}} {{.Image}} {{.Policy}} {{.Updated}}' | grep demo-auto
demo-auto.service f9a574aac7b1 (demo-auto) registry.fedoraproject.org/fedora:latest registry false
$ systemctl --user stop demo-auto

Real output on openSUSE Tumbleweed, podman 6.0.2, 2026-09-28; user and host names and addresses redacted.

false in the last column means the image is current; it becomes pending when the registry has a newer one.

Keep an eye on it

journalctl -u os-update.service -n 50      # last run of os-update
systemctl status zypper-patch.service      # last run of the DIY service
sudo tail -n 30 /var/log/zypp/history      # every package change, with time
sudo snapper list | tail -n 5              # the matching snapshots

A failed run leaves the system unchanged, not broken, but it also means no more updates until someone looks. The journalctl command builder makes a command that shows only failures, and the snapper rollback guide is there for the day an update does go wrong.

Frequently asked questions

Is it safe to run zypper dup automatically on Tumbleweed?

Mostly, with snapper as the safety net. The risk is a change that needs a human decision, such as a package conflict; zypper in non-interactive mode then stops without changing anything. Check the log weekly, and read the factory mailing list when an update keeps failing.

Why does my update service exit with code 103?

zypper updated itself (the package management stack) and wants to be run again for the rest. os-update handles this; a DIY service should run the command a second time or simply catch up on the next run.

Can I choose when the server reboots?

Yes. Set REBOOT_CMD=none in /etc/os-update.conf and reboot yourself, or install and start rebootmgr, which os-update uses automatically and which reboots only inside a maintenance window you configure.