diff --git a/docs/gateway.md b/docs/gateway.md index e20badea3..8b53b38fd 100644 --- a/docs/gateway.md +++ b/docs/gateway.md @@ -188,6 +188,14 @@ Then enable the service: systemctl --user enable --now clawdbot-gateway.service ``` +**Alternative (system service)** - for always-on or multi-user servers, you can +install a systemd **system** unit instead of a user unit (no lingering needed). +Create `/etc/systemd/system/clawdbot-gateway.service`, set `User=` and +`WorkingDirectory=`, then enable with: +``` +sudo systemctl enable --now clawdbot-gateway.service +``` + ## Supervision (Windows scheduled task) - Onboarding installs a Scheduled Task named `Clawdbot Gateway` (runs on user logon). - Requires a logged-in user session; for headless setups use a system service or a task configured to run without a logged-in user (not shipped). diff --git a/docs/setup.md b/docs/setup.md index e8d230c5c..5a580df8f 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -109,6 +109,18 @@ pnpm clawdbot health - Keep `~/clawd` and `~/.clawdbot/` as “your stuff”; don’t put personal prompts/config into the `clawdbot` repo. - Updating source: `git pull` + `pnpm install` (when lockfile changed) + keep using `pnpm gateway:watch`. +## Linux (systemd user service) + +Linux installs use a systemd **user** service. By default, systemd stops user +services on logout/idle, which kills the Gateway. Enable lingering: + +```bash +sudo loginctl enable-linger $USER +``` + +For always-on or multi-user servers, consider a **system** service instead of a +user service (no lingering needed). See `docs/gateway.md` for the systemd notes. + ## Related docs - `docs/gateway.md` (Gateway runbook; flags, supervision, ports) diff --git a/src/commands/systemd-linger.ts b/src/commands/systemd-linger.ts index c1043e73e..5f6af2abb 100644 --- a/src/commands/systemd-linger.ts +++ b/src/commands/systemd-linger.ts @@ -42,8 +42,8 @@ export async function ensureSystemdUserLingerInteractive(params: { params.reason ?? "Systemd user services stop when you log out or go idle, which kills the Gateway."; const actionNote = params.requireConfirm - ? "We can enable lingering now (needs sudo; writes /var/lib/systemd/linger)." - : "Enabling lingering now (needs sudo; writes /var/lib/systemd/linger)."; + ? "We can enable lingering now (may require sudo; writes /var/lib/systemd/linger)." + : "Enabling lingering now (may require sudo; writes /var/lib/systemd/linger)."; await prompter.note(`${reason}\n${actionNote}`, title); if (params.requireConfirm && prompter.confirm) { @@ -60,6 +60,15 @@ export async function ensureSystemdUserLingerInteractive(params: { } } + const resultNoSudo = await enableSystemdUserLinger({ + env, + user: status.user, + }); + if (resultNoSudo.ok) { + await prompter.note(`Enabled systemd lingering for ${status.user}.`, title); + return; + } + const result = await enableSystemdUserLinger({ env, user: status.user, diff --git a/src/wizard/onboarding.ts b/src/wizard/onboarding.ts index 576a5a466..9ad0ef2f7 100644 --- a/src/wizard/onboarding.ts +++ b/src/wizard/onboarding.ts @@ -489,6 +489,17 @@ export async function runOnboardingWizard( nextConfig = applyWizardMetadata(nextConfig, { command: "onboard", mode }); await writeConfigFile(nextConfig); + await ensureSystemdUserLingerInteractive({ + runtime, + prompter: { + confirm: prompter.confirm, + note: prompter.note, + }, + reason: + "Linux installs use a systemd user service by default. Without lingering, systemd stops the user session on logout/idle and kills the Gateway.", + requireConfirm: false, + }); + const installDaemon = await prompter.confirm({ message: "Install Gateway daemon (recommended)", initialValue: true, @@ -539,16 +550,6 @@ export async function runOnboardingWizard( }); } - await ensureSystemdUserLingerInteractive({ - runtime, - prompter: { - confirm: prompter.confirm, - note: prompter.note, - }, - reason: - "Linux installs use a systemd user service. Without lingering, systemd stops the user session on logout/idle and kills the Gateway.", - requireConfirm: true, - }); } await sleep(1500);