Add cachy-auto-update: unattended background updates for CachyOS

A root systemd service applies pacman, AUR, Flatpak and AppImage updates on
its own, gated on battery state, gaming activity and whether anybody else is
using the package system. The CLI is deliberately two switches plus status.

No user password is stored anywhere: pacman runs as root directly, and the AUR
step - which makepkg forbids running as root - drops to a locked system account
that sudoers permits to call pacman without a password.
This commit is contained in:
Felitendo committed 2026-08-08 02:27:04 +02:00
1 parent 327da1dae3
commit ceb024d7be
28 files changed
+3856 -1

No files matched your search

@@ -0,0 +1,13 @@
[Desktop Entry]
Type=Application
Name=CachyOS Auto-Update notifications
Name[de]=CachyOS Auto-Update Benachrichtigungen
Comment=Delivers update notifications that arrived while nobody was logged in
Comment[de]=Stellt Update-Benachrichtigungen zu, die eingingen, während niemand angemeldet war
Exec=cachy-auto-update --deliver-notifications
Icon=system-software-update
Terminal=false
NoDisplay=true
X-GNOME-Autostart-enabled=true
X-GNOME-Autostart-Delay=20
X-KDE-autostart-after=panel
+94
View File
@@ -0,0 +1,94 @@
# cachy-auto-update configuration
#
# One "Key=Value" per line, '#' starts a comment. The file is parsed, never
# sourced, so shell syntax has no special meaning here.
#
# After editing run: systemctl restart cachy-auto-update.timer
# Documentation: man cachy-auto-update
# ---------------------------------------------------------------------------
# Main switches - normally set through `cachy-auto-update enable/disable`
# ---------------------------------------------------------------------------
# Apply updates automatically in the background.
# Off after installation: a freshly installed package should not start
# rebuilding the machine before anybody asked it to.
Enabled=no
# Show a desktop notification after an update, and whenever something needs
# attention.
Notifications=yes
# ---------------------------------------------------------------------------
# When
# ---------------------------------------------------------------------------
# How much time has to pass between two update runs. Accepts a plain number of
# seconds or a suffix of s, m, h, d, w.
# The systemd timer ticks hourly regardless; this is what decides whether a
# tick actually does anything, which is also how a postponed run retries.
UpdateInterval=1d
# Never update while the battery is below this percentage. Ignored on mains
# power and on machines without a battery.
MinBatteryPercent=40
# Update only while plugged in. Stricter than MinBatteryPercent.
RequireAC=no
# Postpone the update while a game is running (GameMode, known game processes,
# or an application blocking idle).
SkipWhenGaming=yes
# ---------------------------------------------------------------------------
# What
# ---------------------------------------------------------------------------
UpdateAUR=yes
UpdateFlatpak=yes
UpdateAppImages=yes
# Also rebuild -git / -devel AUR packages. Off by default: these rebuild
# constantly and break more often than released packages.
UpdateDevel=no
# Which AUR helper to use: auto, paru, yay or pikaur.
AURHelper=auto
# When a new package has to replace or remove an existing one, decide it
# automatically instead of aborting the update. Straightforward replacements
# are handled either way; this covers the case where pacman would otherwise
# stop and ask.
AutoResolveConflicts=yes
# Packages that are never updated automatically, space separated.
# Example: IgnorePkg=linux linux-headers nvidia
IgnorePkg=
# ---------------------------------------------------------------------------
# Housekeeping - all off by default
# ---------------------------------------------------------------------------
# Trim the pacman package cache after an update.
CleanCache=no
# How many old versions per package to keep when CleanCache is on.
KeepOldPackages=3
# Remove packages that were installed as dependencies and are no longer needed.
RemoveOrphans=no
# ---------------------------------------------------------------------------
# Notification detail
# ---------------------------------------------------------------------------
# Say something after a successful update. Nothing is ever shown when there
# was nothing to do.
NotifyOnSuccess=yes
# Report failures.
NotifyOnError=yes
# Point out that a kernel update needs a restart. The machine is never
# restarted automatically.
NotifyReboot=yes
+9
View File
@@ -0,0 +1,9 @@
/var/log/cachy-auto-update/*.log {
weekly
rotate 8
missingok
notifempty
compress
delaycompress
create 0644 root root
}
+19
View File
@@ -0,0 +1,19 @@
# Privilege bridge for unattended AUR updates.
#
# makepkg, paru and yay refuse to run as root, so the update service runs them
# as the "cachy-auto-update" system account. That account then needs to be able
# to hand the finished packages to pacman without a human typing a password.
#
# The account is created by sysusers.d with no password and /usr/bin/nologin:
# it cannot be logged into, and only root can become it. Since the service that
# uses it already runs as root, this rule does not widen the trust boundary -
# it only lets an existing root process take the detour that makepkg demands.
#
# The rule is deliberately not restricted by arguments. paru and yay call
# pacman with argument lists that vary per transaction, so any pattern match
# would give the appearance of a restriction while breaking at random.
Defaults:cachy-auto-update !requiretty
Defaults:cachy-auto-update env_keep += "PACMAN"
cachy-auto-update ALL=(root) NOPASSWD: /usr/bin/pacman
+37
View File
@@ -0,0 +1,37 @@
[Unit]
Description=CachyOS unattended update run
Documentation=man:cachy-auto-update(1)
Documentation=https://github.com/Felitendo/cachy-auto-update
After=network-online.target
Wants=network-online.target
ConditionPathExists=/etc/cachy-auto-update/cachy-auto-update.conf
[Service]
Type=oneshot
ExecStart=/usr/lib/cachy-auto-update/cachy-auto-update-run
# Tool output has to stay parseable; everything shown to a human is rendered
# back into their own locale explicitly.
Environment=LC_ALL=C
# Stay out of the way of whatever the user is actually doing. Building an AUR
# package should never make the desktop stutter.
Nice=19
CPUSchedulingPolicy=idle
IOSchedulingClass=idle
IOSchedulingPriority=7
OOMScoreAdjust=500
# A large AUR package can legitimately take a long time to build.
TimeoutStartSec=4h
# Deliberately *not* sandboxed the way paccache.service is: this unit installs
# packages across the whole filesystem and downloads them over the network, so
# ProtectSystem=, PrivateNetwork= and friends would break it. NoNewPrivileges
# in particular has to stay off - the AUR step relies on the build account
# calling sudo.
NoNewPrivileges=no
ProtectHostname=yes
# No [Install] section on purpose: this is driven by cachy-auto-update.timer,
# never wanted by a target of its own.
+19
View File
@@ -0,0 +1,19 @@
[Unit]
Description=CachyOS unattended update check
Documentation=man:cachy-auto-update(1)
[Timer]
# The tick is hourly, but an actual update only happens once per
# UpdateInterval (default: daily). That combination is what makes a postponed
# run - low battery, a game running, somebody else using pacman - retry by
# itself an hour later without any backoff bookkeeping.
OnBootSec=15min
OnCalendar=hourly
RandomizedDelaySec=20min
AccuracySec=5min
# Catch up after the machine was switched off.
Persistent=true
[Install]
WantedBy=timers.target
+6
View File
@@ -0,0 +1,6 @@
# The account that builds and installs AUR packages.
#
# makepkg, paru and yay all refuse to run as root, so the update service drops
# to this account for the AUR step. It has no password and no shell: nobody can
# log into it, and only root can become it.
u cachy-auto-update - "CachyOS Auto-Update builder" /var/lib/cachy-auto-update/builder /usr/bin/nologin
+15
View File
@@ -0,0 +1,15 @@
# Type Path Mode UID GID Age Argument
# State the CLI reads unprivileged (`cachy-auto-update status`).
d /var/lib/cachy-auto-update 0755 root root - -
# Home of the build account. Everything the AUR helper clones and compiles
# stays in here instead of in somebody's home directory.
d /var/lib/cachy-auto-update/builder 0700 cachy-auto-update cachy-auto-update - -
d /var/cache/cachy-auto-update 0700 cachy-auto-update cachy-auto-update - -
# Logs, matching pacman's own world-readable convention so `log` works without
# root.
d /var/log/cachy-auto-update 0755 root root - -
d /run/cachy-auto-update 0755 root root - -