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
| Leap 16 | Tumbleweed | |
|---|---|---|
| System packages | Daily, low risk: fixes only | Daily or weekly zypper dup, with snapper as safety net |
| Reboots | Only when a patch needs one | Often: most snapshots bring a new kernel |
| Container images | Separately, per container, with podman auto-update | |
Why each flavour uses a different zypper command is in the zypper updates guide.
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.