System Updates
Shanios updates are atomic — the running system is never modified. Updates are written to the inactive slot, verified, and activated on the next reboot. The previous slot is preserved as an instant rollback target.
Automatic Updates
shani-update is the user-facing update manager. It runs automatically via a desktop autostart entry at login (after a 15-second delay) and via a systemd user timer that fires 15 minutes after boot and then every 2 hours.
On each run, shani-update works through a fixed priority sequence:
- Hard failure detection — if a dracut pre-mount hook recorded a
boot_hard_failuremarker (root filesystem failed to mount), offers immediate rollback. This is distinct from a soft fallback-boot and requires manual action. - Fallback boot detection — if the last boot failed and the system fell back to the standby slot, offers to roll back the broken slot.
- Reboot-needed check — if a staged update is waiting, shows a restart dialog.
- Candidate boot check — if you're running a freshly deployed slot, offers a rollback window.
- Update check — fetches release metadata and, if a newer version is available, shows an install dialog.
When the user confirms an update, shani-update detects the available terminal emulator and launches shani-deploy inside it.
Auto-reboot is opt-in here too. Onceshani-deployfinishes successfully — whether launched unattended by the timer or manually — the new slot is ready to boot into whenever convenient; it does not reboot on its own unlessAUTO_REBOOT=yeswas set. See Automatic Reboot After Deployment below.
# Check timer status
systemctl --user status shani-update.timer
# View update manager logs
cat ~/.cache/shani-update.log
journalctl -t shani-update -n 50
# Run an immediate interactive check
shani-update
shani-update Flags
shani-update is mostly a GUI-driven wrapper, but it also accepts CLI flags that mirror (and, for install/deploy actions, pass through to) shani-deploy:
| Flag | Effect |
|---|---|
--startup | Run the login flow: fallback check → reboot-needed check → candidate check → update check |
-r, --rollback | Roll back the inactive slot immediately |
-f, --force | Force deploy even if the version matches or there's a slot mismatch |
-t, --channel <chan> | Update channel for this run: stable or latest |
-v, --verbose | Verbose output from shani-deploy |
-d, --dry-run | Simulate the deployment without changes |
-c, --cleanup | Passthrough: shani-deploy --cleanup |
-o, --optimize | Passthrough: shani-deploy --optimize |
--download-only | Passthrough: shani-deploy --download-only |
--set-channel <chan> | Passthrough: shani-deploy --set-channel (persists to /etc/shani-channel) |
--skip-self-update | Passthrough on install: shani-deploy --skip-self-update |
--update-genefi | Passthrough on install: shani-deploy --update-genefi |
--health [ARGS...] | Forwards remaining arguments to shani-health (e.g. shani-update --health --security) — must be last on the command line |
-h, --help | Show usage |
Running shani-update with no flags does the interactive flow: fallback check → reboot-needed check → candidate-boot check → update check, showing a GUI dialog (yad/zenity/kdialog) at whichever step applies, falling back to a desktop notification or console prompt if no GUI toolkit is available.
Manual Update
# Download, verify, and stage the update
sudo shani-deploy
# Simulate without making any changes (dry-run)
sudo shani-deploy -d
# Force redeploy even if already on the latest version, or if the
# candidate slot doesn't match what's expected (boot mismatch)
sudo shani-deploy -f
# Verbose output
sudo shani-deploy -v
# Override the update channel for a single run
sudo shani-deploy -t latest
# Fetch and verify the update image only — exits before deploying
sudo shani-deploy --download-only
Full Flag Reference
| Flag | Effect |
|---|---|
-h, --help | Show usage |
-r, --rollback | Roll back the non-booted slot (run from the slot you want to keep) |
-c, --cleanup | Manual cleanup of old backups and cached downloads |
-o, --optimize | Manual Btrfs deduplication (bees handles continuous dedup in the background) |
-t, --channel <chan> | Update channel for this run only: stable or latest |
-f, --force | Deploy even if the version matches or there's a boot mismatch |
--download-only | Fetch and verify the update image, then exit without deploying |
-d, --dry-run | Simulate without making changes |
-v, --verbose | Verbose output |
--set-channel <chan> | Permanently persist the channel to /etc/shani-channel |
--skip-self-update | Skip shani-deploy's own auto-update-and-re-exec step |
--update-genefi | Download the latest gen-efi from upstream and use it inside the deploy chroot only (does not install it to the host) |
--download-only cannot be combined with --rollback, --cleanup, --optimize, or --set-channel.
Automatic Reboot After Deployment
The new slot is fully deployed and bootable as soon as shani-deploy finishes — rebooting promptly is not required. By default, shani-deploy does not reboot automatically; reboot whenever it's convenient to switch into the new slot. This applies every time, not just to interactive runs.
# Opt into an automatic reboot 60 seconds after a successful deployment
sudo AUTO_REBOOT=yes shani-deploy
# Change the delay for a single run (seconds)
sudo AUTO_REBOOT=yes AUTO_REBOOT_DELAY=300 shani-deploy
# Cancel a pending automatic reboot (if AUTO_REBOOT=yes armed one)
systemctl stop shanios-auto-reboot.timer
When enabled, auto-reboot is still skipped entirely in --dry-run mode, and is armed via a transient systemd timer unit (shanios-auto-reboot.timer) so it survives even if the terminal running shani-deploy is closed — cancel it with the command above if you need more time before rebooting.
Update Process in Detail
- Self-update check — downloads a newer version of
shani-deployitself if available and re-execs - Slot detection — determines the active and candidate slots
- Space check — verifies at least 10 GB free on the Btrfs filesystem
- Fetch metadata — downloads the latest release manifest from the CDN (R2 primary, SourceForge fallback)
- Download — if a previous image is still cached locally and
zsync2is installed, tries a differential fetch first (only the changed blocks); otherwise streams the full image with resume support viaaria2c,wget, orcurl. See Differential Downloads below. - SHA256 verify — verifies checksum after download, regardless of which download path produced the file
- GPG verify — verifies signature against the Shani OS GPG key (
7B927BFFD4A9EAAA8B666B77DE217F3DA8014792) - Snapshot — takes a timestamped Btrfs snapshot of the inactive slot before writing
- Extract — pipes the verified image into
btrfs receive - UKI generation — runs
gen-efi configure <inactive-slot>inside a chroot of the new slot - Boot entry update — new slot set as next-boot default with
+3-0boot count tries - Notify — writes
/run/shanios/reboot-neededsoshani-updatecan surface a restart dialog on next login - Auto-reboot — opt-in only (
AUTO_REBOOT=yes); the new slot is ready immediately and reboot is left to your convenience by default (see Automatic Reboot After Deployment below)
Nothing in your running OS is touched at any point.
Differential Downloads (zsync2)
Each release image gets a .zsync control file alongside it, generated at build time. When zsync2 is installed and a previous image is still cached in /data/downloads/ (normally left there from your last update), shani-deploy tries a differential fetch first — pulling only the blocks that changed since that cached image — before falling back to a full download.
This is an optimization, not a trust boundary: zsync2 is an actively developing, upstream-experimental tool, so any problem with it (not installed, no cached image to diff against, timeout, failure) silently falls through to the ordinary full download via aria2c/wget/curl. Whichever path produced the file, it still goes through the same SHA256 and GPG verification afterward — a differential download is never trusted on its own, only the verified result is.
There's nothing to configure: this only ever runs against R2 (where the control file's embedded URL always points), and only when a local seed image is actually available.
Rolling Back
# Roll back from the currently booted slot (restores the inactive slot from its last snapshot)
sudo shani-deploy -r
sudo reboot
Or select the (Candidate) entry from the systemd-boot menu at startup (press Space to show the menu).
Important: Run rollback from the OS copy you want to keep. If you are on @blue and want to revert @green, run rollback from @blue.
Update Channels
# Check current channel
cat /etc/shani-channel
# Switch default channel permanently
sudo shani-deploy --set-channel stable # monthly validated builds (default)
sudo shani-deploy --set-channel latest # more frequent, pre-QA releases
# Use a channel for one run only
sudo shani-deploy -t latest
Channel Mechanics
The two channels differ in how builds reach them, not in what they contain:
latestreceives every build that passes automated checks, continuously — as soon as a build is published, it's available on this channel.stableis promoted fromlatest, never built separately. A build only reachesstableafter soaking there through a QA window and passing validation.
Promotion is done via build.sh promote-stable upstream. Because the QA soak is qualitative rather than time-boxed, there's no fixed lag between latest and stable — expect days, not hours, but don't treat any specific number as an SLA.
Switching channels persists to /etc/shani-channel and applies to all future runs:
sudo shani-deploy --set-channel latest
# Check what each remote currently offers before switching
sudo shani-deploy --channel-status stable
sudo shani-deploy --channel-status latest
Recommendation: for personal machines, latest is fine — rollback exists either way. For fleets or mission-critical systems, stay on stable and stagger deployments across machines so one bad build can't hit everything at once.
Boot Counting and Automatic Fallback
After an update, the new slot is registered in systemd-boot with +3-0 boot count tries. If the new slot fails to boot three times, systemd-boot automatically falls back to the previous slot — no user action required.
Shanios uses two tiers of boot failure detection:
| Tier | Marker | Trigger | Action |
|---|---|---|---|
| Hard failure | /data/boot_hard_failure | Root filesystem mount failed (dracut pre-mount hook) | Manual: shani-deploy --rollback |
| Soft failure | /data/boot_failure | System booted but never reached multi-user.target within 15 minutes | Automated rollback offered by shani-update |
On first login after a fallback, shani-update detects the mismatch and shows a dialog offering to roll back the failed slot.
Storage Management
# Remove old backup snapshots and cached downloads
sudo shani-deploy -c
# Run on-demand block deduplication (complements background bees deduplication)
sudo shani-deploy -o
# Check storage health and subvolume sizes
shani-health --storage-info
# Check individual subvolume sizes directly
sudo btrfs filesystem du -s --human-readable /
sudo btrfs filesystem du -s --human-readable /home
sudo btrfs filesystem du -s --human-readable /var/lib/flatpak
Flatpak Auto-Updates
Flatpak apps update separately from the OS. Two timers handle this — one system-wide, one per-user:
# Manual Flatpak update
flatpak update
# View installed Flatpaks
flatpak list --app
# Check Flatpak update timers
systemctl status flatpak-update-system.timer
systemctl --user status flatpak-update-user.timer
The system timer fires 15 minutes after boot and every 12 hours; the user timer fires 20 minutes after boot and every 12 hours, for per-user Flatpak remotes. Both automatically uninstall unused runtimes after updating.
Firmware Updates (fwupd)
sudo fwupdmgr refresh
sudo fwupdmgr get-updates
sudo fwupdmgr update
After a firmware update, PCR 0 changes. Re-enroll TPM2 if you are using automatic LUKS unlock: ``bash sudo gen-efi cleanup-tpm2 sudo gen-efi enroll-tpm2 `` See TPM2 Enrollment.
See Also
- System Health Checks — automated monitoring and diagnostics
- System Config — /etc overlay, locale, hostname, services
- Shell & Environment — Zsh, Starship, Nix, CLI tools
- User Provisioning — automatic group and shell setup
- Shani Reset — factory reset of persistent state
- Blue-Green Deployment — the two-slot architecture
- Troubleshooting — diagnosing update and boot issues