Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Service & Daemon

This page is the operations-side companion to Setup → Service management; that page covers installing and uninstalling the service. This page covers running it: tuning, resource limits, graceful restarts, and multi-workspace setups.

Choosing between user and system scope

ScopeGood forDownside
UserLaptop, single-user dev box, simple deploymentsOnly runs when the user is logged in (Linux with a desktop, macOS) unless you enable lingering
SystemHeadless servers, SBCs, VPSes, multi-user hostsNeeds root to install; gets its own user account

On desktop Linux, enable user-service lingering so the user service persists across logouts:

sh

loginctl enable-linger $USER

Without lingering, a user-scope systemd service stops when the last session closes.

Restart behaviour

The installed systemd user unit (~/.config/systemd/user/zeroclaw.service) uses:

Restart=always
RestartSec=3

systemd restarts the daemon on any exit with a 3-second backoff. There is no exit-code allowlist, so a daemon that fails fast on a bad config will flap; fix the config and systemctl --user restart zeroclaw rather than relying on the service to give up.

On macOS, the LaunchAgent (~/Library/LaunchAgents/com.zeroclaw.daemon.plist) sets RunAtLoad and KeepAlive to true, so launchd keeps the daemon running and relaunches it whenever it exits.

On Windows, zeroclaw service install registers a Task Scheduler task triggered ONLOGON at the LIMITED run level. It starts the daemon at logon; it does not add an automatic restart-on-failure policy.

Graceful shutdown

On Unix the daemon traps SIGINT and SIGTERM; on Windows it traps Ctrl+C (ctrl_c). Any of these triggers a clean shutdown: the daemon stops its channel server and the gateway listener and exits.

SIGHUP is ignored (the daemon stays running). A reload requested via the /admin/reload endpoint restarts the daemon loop in place rather than exiting.

Conversation memory and session state are written to SQLite incrementally during operation, not buffered until shutdown, so a clean stop does not depend on a flush step. Tool receipts are in-band HMAC tokens in the conversation, not a separate on-disk log. A hard SIGKILL skips the clean channel teardown but does not corrupt already-committed memory; only an agent turn that was mid-write is lost.

Manual start for debugging

Skip the service and run the daemon directly:

sh

zeroclaw service stop     # free the gateway port if the service is running
zeroclaw daemon

zeroclaw daemon runs in the foreground, logs to stderr, and is the same process the service runs, just without the service harness. Useful when:

  • Diagnosing startup failures that the service swallows
  • Running under gdb / lldb
  • Testing a config change before committing to it

Terminate with Ctrl-C, same graceful shutdown semantics as SIGTERM.

Resource limits

Linux: systemd

Add to a drop-in:

sh

systemctl --user edit zeroclaw.service
[Service]
MemoryMax=2G
CPUQuota=200%            # two cores
LimitNOFILE=16384        # if opening many channel sockets

Reload and restart:

sh

systemctl --user daemon-reload
systemctl --user restart zeroclaw

macOS: launchd

Edit ~/Library/LaunchAgents/com.zeroclaw.daemon.plist:

<key>SoftResourceLimits</key>
<dict>
  <key>NumberOfFiles</key>
  <integer>16384</integer>
</dict>

Unload + load the plist to apply:

sh

launchctl unload ~/Library/LaunchAgents/com.zeroclaw.daemon.plist
launchctl load ~/Library/LaunchAgents/com.zeroclaw.daemon.plist

Docker

Compose:

services:
  zeroclaw:
    image: ghcr.io/zeroclaw-labs/zeroclaw:latest
    mem_limit: 2g
    cpus: 2.0
    ulimits:
      nofile: 16384

Running multiple workspaces

Each ZeroClaw daemon owns one config directory (which contains its data/ dir). To run two side by side, give each its own config directory via --config-dir (or the ZEROCLAW_CONFIG_DIR env var):

sh

zeroclaw --config-dir ~/.zeroclaw-home daemon
zeroclaw --config-dir ~/.zeroclaw-work daemon

Each instance reads its own config, its own data/ (memory, sessions), its own gateway port (set per config), and its own channel bindings. Memory stays separate; a Telegram bot in one config dir doesn’t know about the other.

zeroclaw service install always installs a single unit pointed at the default config directory; it has no flag to name or parameterize instances. To run more than one as a persistent service, hand-author a second unit file (copy ~/.config/systemd/user/zeroclaw.service to a new name) whose ExecStart passes --config-dir <dir>, then enable it separately.

Don’t point two daemons at the same config directory. SQLite is single-writer; the second will fail on startup.

Observing restarts and crashes

sh

# Linux
journalctl --user -u zeroclaw --since "1 day ago" | grep -E 'Started|Stopped|failed'

# macOS
log show --predicate 'process == "zeroclaw"' --last 1d | grep -E 'start|stop|error'

If you’re seeing repeated restarts, enable debug logging (RUST_LOG=debug via the unit file’s Environment=) and let one more crash happen to capture the full trace.

See also