Lifecycle
Starting and stopping
kache daemon start # start in background, blocks until the socket is ready
kache daemon stop # graceful shutdown (drains pending uploads, up to 30s)
kache daemon restart # restart in place (service-manager kickstart, else stop + fresh spawn)
kache daemon # status: binary version/epoch, service install state, running/stopped, socket path, log location, and (if running) the daemon's version/epoch
kache daemon log # stream the daemon log (follows like tail -f)
kache daemon restart recovers a wedged daemon in three tiers: it first asks the service manager (launchd/systemd) to kickstart it, then falls back to killing any lingering kache daemon run processes and wiping stale socket/lock/state files, then spawns a fresh background daemon.
kache daemon run starts the daemon in the foreground. This is mostly useful for debugging. By default only warnings and errors print to stderr; set KACHE_LOG=kache=info (or kache=debug) for verbose daemon logs.
Installing as a system service
For the daemon to survive reboots and start automatically, install it as a system service.
kache daemon installThis creates a launchd plist at ~/Library/LaunchAgents/ninja.kunobi.kache.plist and loads it. The daemon will start on login and restart if it crashes.
To remove it:
kache daemon uninstallkache daemon installThis creates a systemd user unit (kache.service) and enables it. The daemon runs under your user session.
systemctl --user status kache.service
journalctl --user -u kache.service # logs (or use `kache daemon log`)To remove it:
kache daemon uninstallAutomatic restart on binary update
When you update kache, the old daemon and the new wrapper would disagree on RPC protocol versions. kache handles this automatically.
Each process tracks a build epoch — the modification time of the kache binary. The daemon reads the client's epoch from incoming Upload/Stats/BuildStarted requests; when a client binary is newer, the daemon sets its own shutdown flag and self-terminates once in-flight work drains. If it was installed as a service, launchd/systemd restarts it with the new binary; otherwise the next upload or CLI/monitor command auto-starts it. The next RPC call hits the fresh daemon. You never need to manually restart the daemon after an update.
The daemon applies the same self-restart to config changes. It loads its config once at startup, so it periodically re-fingerprints the active config file; when the file changes on disk (e.g. you edit local_max_size), it schedules the same graceful restart and comes back up with the new config — no manual kache daemon stop needed. Only the config file is watched: a running process's environment is fixed, so KACHE_* env overrides still apply only to freshly started daemons.
Restarting the daemon adds no latency to in-progress rustc invocations: the remote-check hot path never starts the daemon (it skips straight to compilation on a miss). Only the background upload path will lazily (re)start a downed daemon.
Idle shutdown
The daemon can auto-exit after a stretch with no connections, so zombie daemons don't accumulate when you aren't building. It is re-spawned on the next build.
# config.toml
daemon_idle_timeout_secs = 600 # exit after 10 min idle; 0 disables
You can also set KACHE_DAEMON_IDLE_TIMEOUT (seconds). The default is 600 seconds (10 minutes). Set it to 0 to disable the watchdog so the daemon runs indefinitely until you stop it, the binary updates, or the OS signals it.
Lifecycle internals
A few behaviors worth knowing about:
- Single instance: a second
kache daemon runexits cleanly (code 0) if another process holds the run lock or the socket is already live, so launchd/systemdKeepAlivewon't loop. Stale socket files are removed on startup when nothing is listening. - Background log file: auto-started daemons write stderr to
<socket>.lognext to the daemon socket (Linux~/.cache/kache/daemon.log, macOS~/Library/Caches/kache/daemon.log). It is reset to a--- log rotated ---marker once it grows past 2 MiB.kache daemon logtails this file. - Graceful drain: on shutdown the daemon closes its upload queue and waits up to 30 s for in-flight S3 uploads to finish before aborting workers.
Offline behavior
If the daemon is down or unreachable when the wrapper makes an RPC call, the wrapper continues without it. This affects:
- Remote checks: skipped — the wrapper goes straight to compilation on a local miss
- Upload jobs: the wrapper tries to (re)start the daemon and re-send; the job is dropped only if the daemon can't be started. It never fails the build.
- Prefetch: skipped for the current build session
The monitor shows daemon: offline when the daemon hasn't responded in the last refresh cycle. Common causes:
| Symptom | Likely cause |
|---|---|
daemon: offline after reboot | Service not installed, or install is user-scoped and session hasn't started |
daemon: offline after update | Epoch mismatch restart in progress — wait a second and refresh |
daemon: offline continuously | Check kache daemon log for startup errors |
When the monitor shows daemon: offline, it falls back to reading the local store and event log directly, so the Build and Store tabs still show accurate data.