Troubleshooting

Common problems & fixes

Real issues seen setting up the Raspberry Pi kiosk and the in-app updater, with the exact cause and fix for each.

On this page

Kiosk doesn't start on login

Symptom. After speculum-kiosk-enable and a reboot, the mirror doesn't come up fullscreen.

Run the launcher directly from a desktop terminal to surface any error:

/opt/speculum/libexec/speculum-kiosk-run

Cursor shows / screen blanks under Wayland

The kiosk wrapper disables blanking and hides the cursor with xset / unclutter, which are X11-only. On the Wayland Pi desktops (labwc / wayfire) they don't take effect. Fixes:

Two mirrors / port 8080 in use

Symptom. The admin UI / curl localhost:8080/api/version reports an old version even after a successful update; pgrep -af speculum shows a process that predates the update.

Cause. Two mirrors are running at once — e.g. the kiosk autostart and a copy you launched by hand (or a custom systemd unit). They both try to bind port 8080; whichever started first holds it, and the updater may restart/relaunch the one that isn't on screen.

Fix — run exactly one launcher. Stop the extras, then start a single mirror:

pgrep -af speculum                       # see every instance
pkill -f /opt/speculum/bin/speculum      # stop them all
# then start ONE — relaunch the kiosk (log out/in) or run `speculum` once
curl -s localhost:8080/api/version       # confirm one instance, right version

If you made your own systemd unit for the mirror, disable it (sudo systemctl disable --now <your-unit>) and use speculum-kiosk-enable instead.

Updater says "install manually" / "release is unsigned"

Cause. The in-app updater is fail-closed: it only installs a release whose SHA256SUMS carries a valid GPG signature from the key bundled in the app. If the release has no SHA256SUMS.asc (signing not configured in CI), it refuses and points you at the releases page. Development builds also show this.

Fix. Update manually for now. For maintainers: configure the GPG_PRIVATE_KEY / GPG_PASSPHRASE repo secrets (matching KEYS) and cut a release — the release workflow then signs every asset and the updater goes live automatically.

"Couldn't check GitHub for updates"

Update fails: permission denied / read-only / timeout

These were bugs fixed in v0.5.6; if you hit them, update to v0.5.6+ (manually once) and they go away:

Journal lineCause & fix
staged.pkg: Permission deniedOld builds staged into a root-owned dir tied to user pi. Now staged under the mirror user's own ~/.speculum/update. Fixed in v0.5.6.
Read-only file system creating the packageThe updater unit set ProtectHome=read-only, blocking the home-staging write. Removed in v0.5.6.
Request timeout has expired mid-downloadThe HTTP client's default 15 s request cap aborted the ~100 MB download. The downloader now has no whole-request timeout (only a stall timeout). Fixed in v0.5.6.
"could not authorize (polkit)"polkit isn't installed or the rule didn't load. Confirm polkit is present, or use the sudoers fallback. The rule authorizes any active local session to start speculum-update.service.

Inspect the privileged side any time with:

journalctl -u speculum-update.service -n 40 --no-pager

Updated, but the screen still shows the old version

Cause. The package installed, but the running mirror process wasn't the one that got restarted — usually the two-mirrors situation, or the on-screen mirror runs a different launcher than the one the helper restarted.

Fix. Settle on one launch method (above). The helper restarts a system service only if one is active; a --user service or a desktop-session launch is restarted by the app itself. The installed files are already the new version, so simply relaunching the on-screen mirror once brings it to the new release.

How updates work → Kiosk setup →