Autostart
runwisp service install writes a managed systemd user unit (Linux,
WSL) or a launchd LaunchAgent (macOS), enables it, and turns on linger
so the daemon survives logout. No root, no surprise sudo — the only
escalation is loginctl enable-linger, which is printed and confirmed
before it runs.
It never touches runwisp.toml — this is purely OS plumbing.
The four commands
Section titled “The four commands”runwisp service install # wire up systemd / launchdrunwisp service status # show autostart wiring and any driftrunwisp service uninstall # remove the unit (data dir is preserved)runwisp service install --print > custom.service # Ansible / NixDon’t mix up the two status commands: runwisp status answers “is the
daemon alive right now?”, while runwisp service status answers “will it
come back on its own after a reboot?”.
Stop & restart
Section titled “Stop & restart”runwisp stop and runwisp restart are service-aware. When the
daemon runs under a unit installed by service install, they
delegate to the service manager (systemctl --user stop|restart,
or launchctl on macOS) so its view of the unit stays in sync —
a raw SIGTERM would just make systemd restart the daemon behind
your back, and stopping it by hand would leave the manager thinking
it crashed. Without a managed unit, they fall back to a plain
SIGTERM + wait against the PID file.
runwisp restart # the way to apply runwisp.toml editsrunwisp stop # stops the daemon; the unit stays enabled for next bootrestart is also one answer to the “runwisp.toml has changed since
the daemon started” notice that runwisp status, the TUI header,
and the Web UI banner show after you edit the config. For most edits
runwisp reload clears it without bouncing the
process; reach for restart when you’ve changed a restart-only setting
([daemon], scheduler timezone, [storage], [notify], the bind
host/port) or want a fresh boot to re-fire run_on_start and catch-up.
What install does
Section titled “What install does”On Linux and WSL:
- Writes
~/.config/systemd/user/runwisp-<fingerprint>.servicewith the operator’s resolved binary path, config path, data dir, port, and host baked in. The unit name carries the daemon’s human-readable fingerprint (e.g.bright-falcon) so multiple RunWisp instances on the same host install side-by-side. The file starts with a# Managed by runwisp service installmarker so a later re-run can tell whether it can safely overwrite. systemctl --user daemon-reload.sudo loginctl enable-linger <user>— only when linger isn’t already on. This is the single privileged step.systemctl --user enable --now runwisp-<fingerprint>.service.
On macOS:
- Writes
~/Library/LaunchAgents/com.runwisp.daemon.<fingerprint>.plistwith the same managed-marker preamble. The LaunchAgent label matches (com.runwisp.daemon.<fingerprint>), so multiple instances coexist without overwriting each other. launchctl bootout(best effort, in case an old copy is loaded).launchctl bootstrap gui/$UID <path>thenlaunchctl enable.
On WSL, the systemd path runs as on Linux and the command prints a
Register-ScheduledTask PowerShell snippet you paste into a Windows
shell so the distro boots on login.
A confirmation banner lists every action and the resolved settings
before anything is written. --yes skips the prompt for CI; it does
not skip the literal-word confirmation that --purge requires.
Resolving paths
Section titled “Resolving paths”A unit that boots at login can’t rely on your shell or your current
directory, so it needs absolute, durable paths for both the binary and
the data dir. Here’s how runwisp service install works them out:
- Binary:
os.Executable()followed byEvalSymlinks. The installer rejects transient locations outright (/tmp/,/var/tmp/,/dev/shm/, thego-buildcache used bygo run). It warns on awkward-but-acceptable ones (~/go/bin,~/.cache, paths with spaces) and suggests copying to~/.local/bin/runwisp. Override with--binaryfor Ansible/Nix. - Data dir: an absolute
--datais accepted silently. A relative--datais rejected with the resolved absolute path as a hint — a boot-launched unit hascwd=/and would otherwise silently miss the data. The default./.runwispis upgraded interactively: if a DB already exists there, the installer asks whether to keep using it (default yes); otherwise it offers$XDG_DATA_HOME/runwisp(or~/.local/share/runwispif XDG is unset). - Config: same resolution as the daemon. When both
~/.config/runwisp/runwisp.tomland./runwisp.tomlexist, the XDG location wins.
Idempotent re-install
Section titled “Idempotent re-install”Re-running the install command on an unchanged host is a no-op:
| Existing unit | Outcome |
|---|---|
| absent | Install |
| managed, content matches | Noop — exits 0 with Already installed. ✓ |
| managed, content drifted | Update — shows a unified diff, asks before writing |
| hand-written (no marker) | Conflict — refuses; pass --force to overwrite |
The hashes embedded in the unit’s # runwisp-config-hash: and
# runwisp-binary-sha256: lines are how service status notices that
the operator replaced the binary or changed a flag since installing.
service status
Section titled “service status”RunWisp service status
Installed: yes Autostart: enabled Running: yes Unit file: /home/alice/.config/systemd/user/runwisp-bright-falcon.service (matches recorded settings) Binary: /home/alice/.local/bin/runwisp Data dir: /home/alice/.local/share/runwisp (last write 2026-05-20 10:32:01) Linger: on Last start: 2026-05-19 09:00:11 UTC Logs: journalctl --user -u runwisp-bright-falcon.serviceExit codes are part of the contract:
| Code | Meaning |
|---|---|
| 0 | healthy (installed, enabled, running, no drift) |
| 1 | degraded (installed but disabled / stopped / drift) |
| 2 | not installed |
Secrets on systemd
Section titled “Secrets on systemd”runwisp service install never copies
RUNWISP_PASSWORD into the
unit — secrets stay off disk. The recommended pattern is a drop-in
file alongside the managed unit:
Replace <fingerprint> with the slug your runwisp service status
reports for this instance (e.g. bright-falcon):
unit=runwisp-<fingerprint>.servicemkdir -p ~/.config/systemd/user/$unit.dcat > ~/.config/systemd/user/$unit.d/password.conf <<'EOF'[Service]Environment=RUNWISP_PASSWORD=…EOFchmod 600 ~/.config/systemd/user/$unit.d/password.confsystemctl --user daemon-reloadsystemctl --user restart $unitDrop-ins live next to the managed unit but outside the unit file
itself, so service install updates and re-installs do not touch
them.
--print for Ansible / Nix
Section titled “--print for Ansible / Nix”runwisp service install --print writes the rendered unit (or plist)
to stdout and exits 0. It does not touch the filesystem. Combine with
--binary, --config, --data, --port, --host to bake declared
values in:
runwisp service install --print \ --binary /opt/runwisp/bin/runwisp \ --config /etc/runwisp/runwisp.toml \ --data /var/lib/runwisp \ --port 9477 --host 127.0.0.1 > /etc/systemd/system/runwisp-bright-falcon.serviceUse --system to target /etc/systemd/system/ directly from the
interactive command (requires root; documented as advanced — the
default path is the per-user unit).
--purge for full removal
Section titled “--purge for full removal”runwisp service uninstall --purge removes the data dir in addition
to the unit. It is the only place where --yes does not skip a
prompt: the operator must type the literal word delete to proceed.
That guard is permanent; nothing on the command line removes it.
Leave --purge off and uninstall is clean and reversible: it stops the
service, disables autostart, removes the managed unit, runs
daemon-reload, and leaves your data dir exactly where it was.
On WSL, the Linux-side service install runs unchanged — it writes
the systemd user unit and turns on linger. The Windows side then
needs a one-shot scheduled task that boots the WSL distro on login so
the systemd user instance comes up:
$Action = New-ScheduledTaskAction -Execute 'wsl.exe' -Argument '~ -d <distro> -- true'$Trigger = New-ScheduledTaskTrigger -AtLogOnRegister-ScheduledTask -TaskName 'RunWispBoot' -Action $Action -Trigger $Triggerrunwisp service install detects WSL (via WSL_DISTRO_NAME or the
microsoft token in /proc/sys/kernel/osrelease) and prints this
snippet at the end of a successful install with <distro> filled in.
macOS notes
Section titled “macOS notes”The LaunchAgent runs as the logged-in user, on every login. macOS has
no equivalent of loginctl enable-linger — the LaunchAgent is per-user
by design. Logs go to <data-dir>/daemon.log; service status
prints a tail -f hint to the same file.
Flag reference
Section titled “Flag reference”runwisp service install -y, --yes skip the install confirmation (does not skip --purge) --print write the rendered unit to stdout and exit --dry-run print the plan and exit without writing --force overwrite a hand-edited unit --system install /etc/systemd/system/ (Linux, advanced) --binary override the binary path baked into the unit
runwisp service uninstall -y, --yes skip the uninstall confirmation --purge also remove the data dir (typed 'delete' to confirm) --force remove a hand-edited unit