System packages
Sirio lists the updates available on the host and applies them: all at once, only the selected
ones, or the two cleanup operations (autoremove and clearing the cache). It’s the same thing
you’d do with apt from the command line, with the advantage of seeing in one table what’s
behind and how much of that backlog is security-related.
Supported managers
Section titled “Supported managers”Detection looks at which binary is present, not /etc/os-release, so it also works on
derivatives. The search order is apt-get, dnf, yum, apk — dnf before yum because on RHEL 8+ yum
is now a symlink to dnf.
| Manager | Distributions | Notes |
|---|---|---|
apt |
Debian, Ubuntu, Mint, Raspberry Pi OS, Devuan, Proxmox | full support |
dnf |
Fedora, RHEL 8+, Rocky, Alma, CentOS Stream | full; the installed version comes from rpm -qa |
yum |
CentOS 7, RHEL 7 | like dnf, with update instead of upgrade |
apk |
Alpine | listing and upgrading only: see below |
On everything else (pacman, zypper, portage) the section shows up empty and says so.
What differs between managers
Section titled “What differs between managers”The list is always read without privileges, but they don’t all report the same things.
- Installed version. apt and apk put it next to the available one.
dnf check-updatedoesn’t, so it’s derived from a single call torpm -qa; if rpm is missing, the column stays empty and the rest of the list still works. - Security updates. apt infers them from the source archive, dnf and yum from
check-update --security(on very old yum a plugin is needed: without it, none get flagged). Alpine has no such concept: there’s no separate archive to infer it from, so the security counter stays at zero. - Available operations. apk has no
autoremoveand keeps the cache off by default, so it only declares refresh list and upgrade. The page doesn’t show buttons that would fail: it reads the possible actions fromGET /api/packages(thestatus.actionsfield).
Alpine and hosts without systemd
Section titled “Alpine and hosts without systemd”The trigger is a systemd .path unit, which doesn’t exist on Alpine. There, listing still works
— it needs no privileges — but applying updates needs something to run the helper when a request
shows up. The equivalent crontab line is:
* * * * * [ -f /var/lib/sirio/packages/richiesta ] && /usr/local/lib/sirio/sirio-pkgThe protocol is the same: the helper consumes the request and writes the log and result. This applies to any init system other than systemd, not just Alpine.
Why a separate unit is needed
Section titled “Why a separate unit is needed”This is the part worth understanding, because it explains the three files install.sh puts on
the machine.
sirio-sysadmin.service runs as the sirio user, not root, with NoNewPrivileges=true and
ProtectSystem=strict. In that namespace /usr, /etc and /var/lib/dpkg are read-only: apt
wouldn’t work even if Sirio were root, and sudo is blocked outright by NoNewPrivileges.
The only writable directory is SIRIO_DATA_DIR.
Instead of loosening the hardening of the whole service, the privileged operation lives outside it:
panel → /var/lib/sirio/packages/richiesta (single channel, written by Sirio) ↓ sirio-pkg.path sirio-pkg.service (root, oneshot, unsandboxed) ↓ /usr/local/lib/sirio/sirio-pkg → apt-get ↓ job/<id>.log + job/<id>.esito (Sirio reads and shows them)Sirio never runs the helper itself: it writes one file and reads two files. The log is streamed line by line to the page over WebSocket while apt works.
It’s the same reasoning behind why the reverse proxy runs as a container instead of a host service: the hardening stays in place, and whatever needs more privileges moves outside it.
What can be run
Section titled “What can be run”The set is closed and is checked twice: by Sirio before writing the request, and by the helper — which runs as root — before executing it. The request file is written by a process less trusted than the one reading it, so its contents are never taken at face value.
| Action | apt | dnf / yum | apk |
|---|---|---|---|
| Refresh list | apt-get update |
makecache |
apk update |
| Update all | apt-get upgrade |
upgrade (yum: update) |
apk upgrade |
| Update selected | install --only-upgrade -- <names> |
upgrade <names> |
apk upgrade -- <names> |
| Remove unused | apt-get autoremove |
autoremove |
— |
| Clear cache | apt-get clean |
clean all |
— |
None of the update selected variants installs a package that isn’t already present:
--only-upgrade for apt, and for dnf/yum/apk the upgrade verb on a package that isn’t installed
does nothing.
Everything else is rejected. In particular:
- no
dist-upgrade/full-upgrade: they remove packages, and that’s not what you’d expect from an “update” button; - no installing or removing arbitrary packages:
--only-upgradeinstalls nothing new, and the only names accepted are the ones already present in the list of upgradable packages; - names must match
^[A-Za-z0-9][A-Za-z0-9+._-]*(:arch)?$— the union of the Debian, RPM and Alpine naming policies. It’s not one specific manager’s rule, and that’s intentional: what must be impossible is passing something a shell or an option parser could interpret, while whether the name actually exists is established by the check against the upgradable list and by the manager itself; - for apt and apk a
--closes the options, so a name can never become a flag even if the validation failed; dnf5 (Fedora 41+) rejects--afterupgrade, so for dnf/yum it’s the name rule above — a letter or digit first, never-— that keeps names from being options. Nothing is ever concatenated into a shell string: the manager receives a list of arguments; - even the manager to use comes from the request, but the helper re-validates it against its own closed set and checks that the binary exists.
Operations run one at a time: apt takes the dpkg lock regardless, and two in parallel would just produce an unreadable error.
Read-only
Section titled “Read-only”If the helper isn’t installed — install.sh skips it when apt-get isn’t present — the section
still works, but read-only: the list of updates is obtained without privileges
(apt-get -s upgrade), applying them is not. The page says so and disables the buttons.
To install the helper afterwards:
sudo install -d -m 0755 /usr/local/lib/siriosudo install -m 0755 deploy/sirio-pkg /usr/local/lib/sirio/sirio-pkgsudo install -m 0644 deploy/sirio-pkg.service deploy/sirio-pkg.path /etc/systemd/system/sudo systemctl daemon-reloadsudo systemctl enable --now sirio-pkg.pathTo remove it, just do the reverse: systemctl disable --now sirio-pkg.path and delete the three
files. The section goes back to read-only with no other changes.
Reboot required
Section titled “Reboot required”Some updates (kernel, libc) need a reboot to take effect. Sirio shows the warning by reading
/var/run/reboot-required and the packages that created it. The panel doesn’t perform the
reboot: it’s an operation that interrupts the hosted sites, and has to be decided by whoever
knows when they can afford it.
If something doesn’t add up
Section titled “If something doesn’t add up”| Symptom | Likely cause |
|---|---|
| “no response from sirio-pkg” | the unit isn’t active: systemctl status sirio-pkg.path |
| “apt exited with code 100” | the dpkg lock is held, often by unattended-upgrades. The full output is in the operation’s log |
| List stuck on an old date | an apt-get update is missing: that’s the Refresh list button |
| The section is empty on Debian | SIRIO_PACKAGES_ENABLED=false, or apt-get isn’t in the service’s PATH |
The activity log records who requested what; the operations history keeps the log and result of
the last SIRIO_PACKAGES_RETENTION runs.
Coexisting with unattended-upgrades
Section titled “Coexisting with unattended-upgrades”They don’t conflict: unattended-upgrades keeps doing its job, and Sirio sees the result on the
next list refresh. The only friction is the dpkg lock when the two happen at the same time — in
that case the panel’s operation fails with an explicit error and can be retried.