systemd Service
The netclaw daemon (netclawd) runs as a systemd user service — no sudo, no root, just your own account. One CLI command sets everything up.
Before you begin
Section titled “Before you begin”- Linux with systemd 236+ (Ubuntu 20.04+, Debian 11+, Fedora 36+, RHEL 8+, etc.) — check with
systemctl --version dbus-user-sessioninstalled — required forsystemctl --useron headless/minimal servers (e.g.,sudo apt install dbus-user-sessionon Debian/Ubuntu, then re-login)- Netclaw installed — see Installation if you don’t have it yet
- An initialized
~/.netclawdirectory — runnetclaw initfirst - A configured provider and model — see Models
Install the service
Section titled “Install the service”netclaw daemon installThis writes a unit file to ~/.config/systemd/user/netclaw.service, enables it, and runs loginctl enable-linger $USER so the service survives logout.
Start it:
systemctl --user start netclawVerify:
netclaw status
The unit file
Section titled “The unit file”netclaw daemon install generates this at ~/.config/systemd/user/netclaw.service:
[Unit]Description=Netclaw DaemonAfter=network.target
[Service]Type=simpleExecStart=/path/to/netclawdExecStop=/path/to/netclaw daemon stopRestart=alwaysRestartSec=5Environment=DOTNET_ENVIRONMENT=Production
[Install]WantedBy=default.targetThe /path/to/ placeholders are resolved to actual binary locations by netclaw daemon install — the literal string never gets written. ExecStop uses the CLI’s daemon stop command instead of a raw signal, which lets the daemon fire shutdown webhooks and drain active sessions before exiting. After ExecStop completes, systemd sends SIGTERM with a 10-second grace period. If the process is still alive after that, SIGKILL finishes it. In practice the daemon shuts down well within that window.
Manual installation
Section titled “Manual installation”If you prefer to create the service file yourself, adjust the ExecStart and ExecStop paths to wherever your binaries actually live:
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/netclaw.service << 'EOF'[Unit]Description=Netclaw DaemonAfter=network.target
[Service]Type=simpleExecStart=/path/to/netclawdExecStop=/path/to/netclaw daemon stopRestart=alwaysRestartSec=5Environment=DOTNET_ENVIRONMENT=Production
[Install]WantedBy=default.targetEOF
systemctl --user daemon-reloadsystemctl --user enable netclaw.serviceloginctl enable-linger $USERsystemctl --user start netclawThe %h specifier (expands to your home directory) also works in ExecStart/ExecStop paths if you prefer relative-to-home references.
Service management
Section titled “Service management”| Action | Command |
|---|---|
| Start | systemctl --user start netclaw |
| Stop | systemctl --user stop netclaw |
| Restart | systemctl --user restart netclaw |
| Status | systemctl --user status netclaw |
| Logs (journal) | journalctl --user -u netclaw |
| Enable on boot | systemctl --user enable netclaw |
| Disable on boot | systemctl --user disable netclaw |
The CLI commands netclaw daemon start, netclaw daemon stop, and netclaw daemon status also work, regardless of whether systemd is managing the process.
Lingering
Section titled “Lingering”systemd kills user services when you log out. Lingering prevents that by keeping your user slice alive.
# Check if lingering is enabledls /var/lib/systemd/linger/$USER
# Enable it (netclaw daemon install does this automatically)loginctl enable-linger $USERIf your daemon stops every time you disconnect SSH, lingering isn’t enabled.
Configuration
Section titled “Configuration”Config lives at ~/.netclaw/config/netclaw.json. To relocate the base directory, set NETCLAW_HOME — useful when you want data on a separate volume or need to run multiple instances with isolated state. Add it to the [Service] section of the unit file:
[Service]Environment=NETCLAW_HOME=/data/netclawKey paths
Section titled “Key paths”| Path | Contents |
|---|---|
~/.netclaw/config/netclaw.json | Daemon settings |
~/.netclaw/config/secrets.json | Provider API keys and credentials |
~/.netclaw/netclaw.db | SQLite database (sessions, stats) |
~/.netclaw/logs/daemon.log | Rolling log file |
~/.netclaw/netclaw.pid | PID file |
~/.netclaw/netclaw.lock | Singleton lock (OS-backed exclusive lock) |
Network binding
Section titled “Network binding”Default binding is 127.0.0.1:5199, loopback only.
{ "Daemon": { "Host": "127.0.0.1", "Port": 5199 }}To bind a different address or port via environment variables, add them to the unit file:
[Service]Environment=NETCLAW_Daemon__Host=127.0.0.1Environment=NETCLAW_Daemon__Port=5200Then reload and restart:
systemctl --user daemon-reloadsystemctl --user restart netclawFor remote access via Tailscale or Cloudflare Tunnel, see Exposure Modes.
Config reload
Section titled “Config reload”A file watcher on netclaw.json picks up changes and triggers a graceful drain-and-restart automatically. One exception: Daemon.Host, Daemon.Port, and Daemon.ExposureMode require a manual systemctl --user restart netclaw — the file watcher ignores these fields because changing the listener binding mid-flight isn’t safe.
Health checks
Section titled “Health checks”The daemon exposes two HTTP endpoints:
| Endpoint | Auth | Purpose |
|---|---|---|
GET /api/health/ready | None | Returns 200 OK when the daemon is ready |
GET /api/health/status | Required | Detailed runtime status |
Smoke test:
curl -sf http://127.0.0.1:5199/api/health/ready && echo "OK"To block systemd from reporting the service as “started” until the daemon is actually ready, add an ExecStartPost readiness gate to your [Service] section. This delays dependent services until netclaw is genuinely accepting connections:
ExecStartPost=/bin/sh -c 'until curl -sf http://127.0.0.1:5199/api/health/ready; do sleep 2; done'For richer metrics and log export, see OpenTelemetry.
Logging
Section titled “Logging”Logs go to ~/.netclaw/logs/daemon.log as a rolling file — journalctl --user -u netclaw captures stdout/stderr from the process, but the structured application logs live in the file. Change the log level via Logging:LogLevel:Default in netclaw.json:
{ "Logging": { "LogLevel": { "Default": "Information" } }}Valid levels: Trace, Debug, Information, Warning, Error, Critical.
Follow the log in real time:
tail -f ~/.netclaw/logs/daemon.logUpgrading
Section titled “Upgrading”Schema migrations are forward-only with no automatic rollback, so back up before upgrading.
Unlike the Docker deployment, bare-metal installs can self-update — the daemon periodically checks for new releases and applies them. After an update, the process exits and systemd’s Restart=always brings it back on the new version. For manual upgrades:
# 1. Stop the daemonsystemctl --user stop netclaw
# 2. Back up the databasecp ~/.netclaw/netclaw.db ~/.netclaw/netclaw.db.bak.$(date +%s)
# 3. Replace binaries (method depends on how you installed)# For manual installs, download and extract the new release.# For package manager installs, update the package.
# 4. Start the daemonsystemctl --user start netclaw
# 5. Verifyuntil curl -sf http://127.0.0.1:5199/api/health/ready; do sleep 2; doneecho "Daemon is ready"To rollback: stop the daemon, restore the database backup, put the old binaries back, and start again.
Uninstalling
Section titled “Uninstalling”netclaw daemon uninstallYour data in ~/.netclaw stays untouched.
To remove manually:
systemctl --user stop netclawsystemctl --user disable netclawrm ~/.config/systemd/user/netclaw.servicesystemctl --user daemon-reloadTroubleshooting
Section titled “Troubleshooting”Start here: netclaw doctor
Section titled “Start here: netclaw doctor”Before diving into specific symptoms, run netclaw doctor. It checks provider connectivity, config validity, daemon health, and common misconfigurations in one pass.

Daemon stops after SSH disconnect
Section titled “Daemon stops after SSH disconnect”Lingering isn’t enabled. Fix it with loginctl enable-linger $USER and verify with ls /var/lib/systemd/linger/.
”Failed to connect to bus” when running systemctl
Section titled “”Failed to connect to bus” when running systemctl”XDG_RUNTIME_DIR isn’t set. Common when running systemctl --user from cron or a non-login shell. On headless servers, also make sure dbus-user-session is installed (sudo apt install dbus-user-session). Export the runtime dir manually:
export XDG_RUNTIME_DIR=/run/user/$(id -u)systemctl --user status netclawService starts but CLI can’t connect
Section titled “Service starts but CLI can’t connect”Check that the daemon is actually listening:
ss -tlnp | grep 5199If nothing shows, check the daemon log:
tail -20 ~/.netclaw/logs/daemon.logCommon causes: port conflict (another process on 5199), JSON syntax error in netclaw.json, or missing provider configuration.
”Daemon already running” when starting
Section titled “”Daemon already running” when starting”The lock file at ~/.netclaw/netclaw.lock is held by another process — either a daemon is already running or a previous instance didn’t exit cleanly. Check for it:
pgrep -a netclawdIf nothing shows, the lock file is stale. Remove it and restart:
systemctl --user stop netclawrm ~/.netclaw/netclaw.locksystemctl --user start netclawService fails on older systemd versions
Section titled “Service fails on older systemd versions”User-level services require systemd 236+. Check with systemctl --version. On older systems, skip systemd and run netclaw daemon start directly — it detaches as a background process.
Related pages
Section titled “Related pages”- Docker Deployment — containerized alternative
- Exposure Modes — remote access via Tailscale or Cloudflare Tunnel
- Models — model slot configuration
- OpenTelemetry — metrics and log export
netclaw doctor— built-in health check diagnostics
Resources
Section titled “Resources”- systemd user services documentation — full unit file reference
- systemd user services on ArchWiki — best practical guide for user-level systemd
- loginctl enable-linger — why lingering matters for headless services
- .NET environment variable configuration — the double-underscore nesting convention used by
NETCLAW_env vars