OpenClaw on Linux: Gateway Support and Desktop Companion
Covers running the OpenClaw Gateway on Linux with Node as the default runtime and the Tauri desktop companion for local and remote Gateways. Useful for Linux users installing and managing OpenClaw.
Read this when
- Looking for Linux companion app status
- Enabling camera, location, or notifications on a Linux node host
- Planning platform coverage or contributions
- Debugging Linux OOM kills or exit 137 on a VPS or container
The Gateway runs on Linux with full support. Node is the default, primary, and recommended runtime; builds of Bun 1.4.3+ that are WAL-reset-safe with node:sqlite can run OpenClaw as an explicit opt-in. For installing dependencies, use pnpm instead of Bun.
Desktop companion
The OpenClaw Linux companion is a Tauri desktop app that works with local and remote Gateways. On Windows, the native Windows Hub is recommended; this cross-platform Tauri app serves as the backup, shipping in regular OpenClaw releases with bundled Bun for a local Windows Gateway. On Linux, it:
- sets up a local Gateway automatically on fresh installs, with manual local, discovered remote, direct URL, and SSH choices offered if setup fails
- installs the OpenClaw CLI and runs fresh local installations on the bundled OpenClaw Bun fork, with no global CLI install required; release builds install their matching stable version, while development builds use Development for automatic setup
- attaches to a healthy Gateway before trying any service changes
- hands install, start, stop, and restart operations to the CLI-managed systemd user service
- discovers nearby Bonjour Gateways and opens each Control UI in a route-scoped window, so multiple Gateway dashboards can stay connected and be used at the same time
- opens the Gateway-served Control UI using its resolved authentication URL
- opens custodian onboarding after automatic local setup to prepare AI access and explain the selection; manual setup keeps the explicit Model Setup picker
- continues into guided onboarding once a new model is connected; onboarding can import detected Claude Code, Codex, or Hermes memories into the agent workspace (the same import remains available later under Settings → Import Memory)
- stays reachable from the system tray after its window is closed
Window controls sit in the dashboard's top row. To move the window, drag empty header space or a session title, and double-click to maximize or restore it. The thin strip below the top resize edge also moves the window. Minimize, maximize/restore, and close are at the top right; the window edges stay resizable. Closing the main window leaves OpenClaw available in the system tray. When you connect to an older Gateway whose dashboard does not support this layout, the companion keeps the system title bar. Update the Gateway to enable the unified window controls.
The local startup, setup, recovery, Gateway manager, and Quick Chat screens share light and dark styling and follow system appearance changes while open. Connection drafts, credential visibility, and Quick Chat replies stay intact. The connected dashboard keeps its own web UI appearance setting.
Remote setup and Connection Settings use a single Authentication choice for token or password. Show credential reveals the entered value; switching types clears the draft and masks the new field. Press Enter or Connect to Gateway to connect. In Connection Settings, blank credentials reuse the saved credentials for the same endpoint.
Chrome extension setup
At startup and after CLI installation, the app prepares the local Chrome native helper. Release builds reuse a matching CLI or install a version-matched browser runtime under their own app-data directory. That download does not create, check, refresh, or restart a Gateway service, replace its runtime, or change the selected remote connection. An internet connection is required.
Choose Set Up Chrome Extension… in the tray to retry setup and open the official Chrome Web Store listing once native registration succeeds. Google Chrome on Linux still requires Add to Chrome in the Store; the app does not use enterprise force-install policies or reopen the Store at every startup. Once enabled, supported host-local setups pair automatically without a copied credential. A remote-only desktop connection still needs a browser node on this computer to expose its tabs to the remote Gateway.
Development builds use an existing local CLI rather than downloading an unrelated stable runtime. The Windows Tauri build does not provide this runtime installer. See Chrome extension for approval, disconnection, and manual recovery.
Desktop compatibility
The Linux companion shares one Bun fork pin with the native macOS app and CI. Runtime admission, SQLite safety checks, and the fork's disabled implicit package auto-install remain enabled. macOS Tauri test builds keep their existing runtime behavior; the native macOS app owns its separate bundled runtime. Windows Tauri releases use the signed Windows fork from the shared pin for local setup; an unsigned dry-run is not shippable. See Bun compatibility.
Published AMD64 AppImages are built on Ubuntu 22.04 and require glibc 2.35 or newer plus a libstdc++ that provides GLIBCXX_3.4.30. Ubuntu 22.04 and Debian 12 meet that ABI floor. RHEL 9 and Rocky Linux 9 ship glibc 2.34, so they cannot run the published AppImage. Extracting the AppImage does not bypass this requirement.
.deb installs stay owned by the system package manager; installing the download does not add an APT repository. AppImages use the signed in-app updater.
Global shortcuts are available on X11. On Wayland, use the tray's Quick Chat entry when your desktop provides a tray host; global shortcuts are unavailable. Tray access is a shortcut fallback, not a native Wayland compatibility guarantee.
The shell does not grant microphone capture to its embedded WebKitGTK WebView, so getUserMedia is expected to fail there. Open the Gateway's Control UI in a regular browser for Talk mode.
The desktop connects as a Gateway operator and uses the local CLI to share this computer's desktop with its Primary Gateway. Its app-owned node exposes desktop streaming only. Other device commands belong to the CLI node host and its Linux Node plugin.
The native macOS app and Windows Hub are separate applications. Windows Hub is the recommended Windows app; this Tauri shell is the backup. The macOS Tauri build remains an opt-in test bundle. See the platform pages for requirements and capabilities.
Gateway selection
Open Gateways → Manage Gateways… from the native app or tray menu to save a direct URL or SSH connection. Choose Add Gateway or Edit to open the connection form; Back to Gateways returns to the saved list and discards unsaved changes. Under Authentication, choose token or password and enter a credential only if needed. Saved credentials stay hidden; leave the field blank to keep them for the same connection. Switching authentication types clears the credential you have entered. SSH certificate pins are under Advanced connection settings.
The dashboard's profile menu switches only its current window; Control-click opens an additional window. Choosing a Gateway from the native menu focuses its existing window without reloading it, while Open … in New Window creates an independent one.
The Primary Gateway continues to own Quick Chat and the desktop connection. Changing it requires the separate Set as Primary confirmation on a saved token-authenticated connection. Other Gateway windows retain their own targets. The companion remembers successful explicit selections, returns to Primary when that saved connection is removed, and keeps credentials in the operating system's credential store. Linux requires an unlocked Secret Service, such as GNOME Keyring or KWallet's Secret Service support.
An unavailable credential store shows a dismissible notice without blocking the dashboard. Saved connections remain intact; use Manage Gateways… → Try again after resolving the reported credential-store problem.
When a saved Gateway fails to load, the same window returns to its local connection editor. Correcting the endpoint updates the remembered selection only after the new dashboard loads successfully.
The macOS Tauri build is named OpenClaw-Tauri and keeps its saved connections separate from the native OpenClaw app.
Use the bundled runtime
On Linux, a new local setup puts the Gateway on the bundled OpenClaw Bun fork by way of the standard CLI. The install guard needs the service to be absent still. If a service shows up during setup, that installation is blocked. The app's runtime marker serves an informational purpose only and never grants permission for automatic service changes.
Should an existing Gateway run on a different runtime, pick Use bundled runtime… from the tray menu. That covers Node, an older bundled Bun left over after an app update, and a runtime the operator selected. The confirmation displays the runtime currently in use. Before switching to the current bundled Bun, the CLI install verifies that the service definition and runtime pin still match what you confirmed. When either one has changed, the action is refused. A Gateway that is paused or stopped remains stopped, so choose Start Gateway first if you want to switch runtimes.
Once installation finishes, the app checks Gateway health. If the check fails, the error appears along with a CLI command that returns you to the previous runtime. Automatic restoration of a runtime does not happen. To go back to Node, install a supported Node version when needed, then run:
openclaw gateway install --force --runtime node
To return to one specific earlier executable, use openclaw gateway install --force --runtime-path /absolute/path/to/node-or-bun. Service installation belongs to the CLI. Changing the runtime of an existing Gateway leaves its CLI launcher as it was.
Neither startup nor app updates modify an existing Gateway service or its runtime pin. Following an app update, the current service keeps whatever runtime it already had until you select Use bundled runtime… once more. App runtime directories are immutable and are retained, so an update never deletes a runtime that a service references. These actions exist only on Linux; macOS Tauri retains its prior behavior, which is separate from the native macOS app.
Desktop sharing
To adjust the setting, open Settings → This computer → Capabilities → Desktop sharing. In the macOS Tauri build, this section is labeled This Mac. Sharing is on from the start; an existing desktop.host.enabled: false remains off until you enable it explicitly inside the app. Whatever you choose survives app restarts and works independently of Keep computer awake.
A local OpenClaw CLI is required for sharing, even when your Gateway is remote, along with an authenticated local VNC server. On macOS, turn on Screen Sharing under System Settings. When the Primary Gateway asks, approve that computer's desktop capability, then open its desktop from Systems. Authentication, pairing, and upgrade behavior are covered in paired node desktops.
The status row indicates whether the app's desktop process is running or requires attention. The desktop cannot open until pairing approval and the local VNC server are also ready. Errors for a missing CLI or an invalid configuration show up here. Disabling sharing, switching Primary Gateway, or quitting the app ends the old desktop connection. If you close the window to the tray, sharing stays active.
First-run setup
When no OpenClaw configuration or Gateway profiles have been saved, local setup launches on its own and custodian onboarding opens in the Control UI. The custodian arranges whatever AI access is available and explains both the selection and the alternatives. Use a different Gateway takes you to Manage Gateways, Review permissions opens this computer's permissions, and Open AI setup opens Model Setup. On the card, the local Gateway is labeled This computer while saved remote Gateways appear by name; native actions keep the base path of the Gateway you selected.
Automatic setup relies on the same bundled runtime, installer, and CLI-managed service that manual local setup uses. It never joins a discovered Gateway on its own. Configurations that already exist and remote setups behave as they did before. If the saved profile store cannot be read, or automatic setup fails for any reason, you are returned to the manual choices with the error shown.
During manual setup, decide where your assistant should live:
- On this computer installs any local prerequisites that are missing and launches the Gateway as a systemd user service.
- On another computer attaches to a Gateway that already exists. Pick a discovered Gateway, type its address into Gateway URL, or select SSH tunnel and supply an SSH target like
user@gateway-host. The Gateway port is18789unless you change it.
Should the remote Gateway need authentication, expand Gateway authentication and supply its token or password. Pick a single credential type that matches how the remote Gateway is configured. Remote setup neither installs nor starts a local Gateway service; the remote host is responsible for its model, provider credentials, and agent state.
For public direct connections, use HTTPS or wss://. Restrict plain HTTP or ws:// to loopback, trusted private networks, and Tailnet hosts. If the saved configuration contains gateway.remote.tlsFingerprint, pick SSH tunnel rather than connecting directly. Because the embedded browser cannot enforce a certificate pin, the app refuses direct connections before it loads the remote dashboard or exposes its credentials. Saved remote token and password values may use environment- or file-backed SecretRefs; exec and shared-store references have to be resolved on the Gateway host that owns them. SSH relies on your existing OpenSSH authentication and host-key verification. Secure Gateway configuration is described in Remote access.
Once a manual connection succeeds, Model Setup finds the AI access the selected Gateway can reach and offers it as a choice. Nothing is imported or copied from an account during discovery. On a first visit, the companion selects, tests, installs, and saves no provider until you trigger its action. Provider sign-in or API-key entry is presented when it is needed, and a successful model response must occur before the agent opens. A Gateway that is already configured opens its usual dashboard after verification, while newly configured access proceeds into guided onboarding.
If the Gateway reports that a live model test failed before the model and credentials were saved, dismiss the error and retry or pick a different connection. An uncertain error leaves replacement setup blocked, since settings may already have been written. Confirmed cancellation, and requests that were rejected before setup began, can be retried right away.
Model Setup may resume an activation through a Gateway restart or an app reopen as long as its temporary recovery record remains valid. Recovery stays tied to the same Gateway, agent, and authentication. Where the known activation target still matches the selected model, OpenClaw verifies that exact model before guided onboarding continues, instead of activating the provider once more. For an unresolved result, either use Verify & use selected model to explicitly verify and adopt a displayed model, or wait until the setup attempt's bounded window ends and then choose Check again. Recovery is not guaranteed once that record expires, when browser storage is cleared or becomes unavailable, or if the Gateway, agent, or authentication changes.
Ollama automatic discovery draws on eligible models already loaded in memory, not every model installed on disk. To use an idle installed model, pick Choose connection on its Ollama card, then Local only. See Ollama.
For OpenAI, pick ChatGPT Login to use a ChatGPT or Codex subscription, or OpenAI API Key for API billing. Browser sign-in finishes on the Gateway host. If that host is remote, or its localhost callback is unreachable, pick ChatGPT Device Pairing from the additional sign-in options instead; device pairing needs no localhost callback. See OpenAI and OAuth.
When the desktop app launches with a supported provider API key in its environment, the Gateway service stores that dedicated inference credential in an owner-only environment file. Provider admin keys, GitHub tokens, and unrelated environment variables are never copied into the service.
Host sleep
To stop idle sleep while the desktop companion runs, including when its windows are closed, choose Keep computer awake in the native tray menu. The setting is off by default and your choice is remembered across app restarts. Disabling it or quitting OpenClaw releases the keep-awake request. It neither alters your permanent power settings nor unlocks the computer. If the operating system cannot honor a saved request, the menu flags the checked preference as inactive and reports the error. Unchecking it still turns the saved preference off.
On Linux, idle inhibition comes from GNOME's session manager or from an xdg-desktop-portal backend that implements it. Some desktops will additionally block display dimming and automatic locking this way, though locking by hand still works. The optional macOS and Windows Tauri builds stop the system from going to idle sleep, but they do not ask for the display to stay lit.
Where systemd-logind is present, the companion sets up a suspension lease for its local Gateway before the host goes to sleep. On wake it reconnects and brings the Gateway back up; remote Gateway routes are not modified. Should logind or the system bus be missing, the sleep hook turns itself off and the app keeps running as usual.
Stable releases built from main or from the matching release/YYYY.M.PATCH branch publish .deb and AppImage bundles as assets on the GitHub release for the tag, named OpenClaw-<version>-amd64.deb and OpenClaw-<version>-amd64.AppImage, with a SHA256SUMS.linux-app.txt checksum file beside them. Grab the .deb and install it using sudo apt install ./OpenClaw-<version>-amd64.deb, or make the AppImage executable and launch it straight away. The AppImage runtime requires FUSE 2 (sudo apt install libfuse2, or libfuse2t64 on Ubuntu 24.04+); if it is absent, start the AppImage with APPIMAGE_EXTRACT_AND_RUN=1.
A regular stable publication requests Linux bundles on its own once the Gateway release is visible. On Linux, building, signing, and publication each complete independently. While those bundles are still pending, the app updater keeps offering the last published Linux version through its original signed download.
Only download a release that includes the named Linux bundles and checksum file; the arrival of a new Gateway release by itself does not mean a new Linux app exists. The updater that ships still relies on releases/latest/download/latest.json. Separate linux-stable publication tooling is neither a client endpoint nor a download-link migration. Turning it on calls for its own release approval plus signed proof from an installed client; see Linux companion publication.
Media codecs
Audio and video playback in the companion relies on GStreamer plugins. WebM/VP9, Opus, Vorbis, and WAV generally work via plugins-good. For H.264/MP4, AAC, and MP3 you need the libav and/or plugins-bad packages. The .deb draws on the host's plugins and lists all three packages as dependencies. Inside the AppImage, the GStreamer media framework and the plugins for those formats are bundled. When building from source, or when rebuilding either Linux bundle, install the packages and the inspection tool explicitly:
sudo apt update && sudo apt install gstreamer1.0-libav gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-tools patchelf xdg-utils unzip
Before Tauri calls linuxdeploy, the packaging script stages just that media capability set. That way optional host plugins cannot pull unrelated system libraries into the AppImage dependency closure.
The packaging flow installs Tauri's five AppImage tools into a clean cache pinned by digest. Once Tauri has built the AppImage, the finalizer checks that cache again, strips bundled Wayland client libraries from the retained AppDir, and rebuilds the artifact. WebKitGTK and Mesa then share one compatible host stack.
Building the same bundles from a source checkout is also possible:
plugins=$(mktemp -d)
cache=$(mktemp -d)
trap 'rm -rf "$plugins" "$cache"' EXIT
export XDG_CACHE_HOME="$cache"
apps/linux/scripts/stage-appimage-gstreamer.sh "$plugins"
apps/linux/scripts/tauri-appimage-tools.sh prepare
apps/linux/scripts/tauri-appimage-tools.sh verify pre-build
export LDAI_RUNTIME_FILE="$(apps/linux/scripts/tauri-appimage-tools.sh runtime-path)"
(
cd apps/linux/src-tauri
GSTREAMER_PLUGINS_DIR="$plugins" \
pnpm dlx @tauri-apps/cli@2.11.4 build --bundles deb,appimage \
--config '{"bundle":{"createUpdaterArtifacts":false,"useLocalToolsDir":false}}'
)
apps/linux/scripts/finalize-appimage.sh \
apps/linux/src-tauri/target/release/bundle/appimage
Affected pull requests are checked by the Linux App workflow using Rust tests, native builds, and the native inline-browser smoke test; bundles are not built for pull requests. A manual run builds the .deb and AppImage and uploads them as the openclaw-linux-companion workflow artifact; no release is published. Linux build dependencies and development commands are documented in apps/linux/README.md in the repository.
Quick Chat
A new session is opened by Ctrl+Shift+O only in the dashboard that has focus. This chord is not reserved globally by the companion, so other foreground apps keep their own shortcut behavior.
Open Quick Chat with Ctrl+Shift+Space or through the Quick Chat tray item. The agent chip displays the configured avatar, emoji, or monogram; pick it to switch agents. Messages go to the selected agent's main session and respect global session scope. A persistent Ed25519 device identity is owned by the native Rust client. The shared token or password from the CLI handoff is used only to bootstrap pairing; on later connections the Gateway-issued device token is stored and preferred. Both the identity and the device token sit in the app config directory in a file with mode 0600; neither credentials nor the WebSocket reach Quick Chat's WebView.
If the native connection drops, Quick Chat displays Gateway unreachable, retrying and blocks sending until it reconnects. A remote device that has entered the pairing phase instead shows Approve this device in the dashboard (Nodes), along with a short device ID when the Gateway supplies one. When a Gateway needs a shared credential that is missing, it shows Gateway requires a credential, open the dashboard on the gateway host; in that state no pairing request is awaiting approval. Where the server provides remediation guidance that is more specific, it replaces these fallback notices.
For TLS Gateways, the CLI passes the app the SHA-256 fingerprint of the Gateway certificate; the native client pins that certificate and reports Gateway TLS trust failed, check the certificate fingerprint as a separate condition from downtime. When a Gateway's shared secret is set through a SecretRef, it is left out of the CLI handoff. Installs that are already paired keep working via their stored device token, but under shared-secret authentication a fresh install cannot create a pending pairing request without that bootstrap credential.
Setup-code and bootstrapToken redemption require dedicated product UI and are still a follow-up; Quick Chat does not attempt either flow.
On X11, record or reset a custom shortcut with the gear in Quick Chat. The Quick Chat shortcut tray toggle turns it on or off without disabling the plain Quick Chat tray item. Wayland offers no global shortcuts, so the shortcut settings stay hidden and the tray item remains the way in. After a send is accepted, Quick Chat remains open and streams the selected agent's plain-text reply above a single bottom composer, with your submitted message shown next to the reply. Collapse the reply for a compact composer; expanding it brings back the live text and any widget contents. A next draft can be prepared while a reply streams, then sent once the turn ends. Return sends, Shift-Return inserts a newline, and Ctrl+Enter sends and opens the dashboard. Open dashboard also sits beside the composer controls. Press Esc to dismiss the bar and its reply.
CLI and SSH alternative
For a headless server or VPS, the CLI is still the simplest route. To connect without the Linux desktop companion, use a manual SSH tunnel:
- Install Node 26 (recommended), or another supported release: Node 24.16+ or Node 26.1+.
- On npm 12 or npm 11.16+, run
npm i -g openclaw@latest --allow-scripts=openclaw. On npm 11.15 and earlier, omit--allow-scripts=openclaw. openclaw onboard --install-daemon- From your laptop:
ssh -N -L 18789:127.0.0.1:18789 <user>@<host> - Open
http://127.0.0.1:18789/and authenticate with the configured shared secret (token by default; password ifgateway.auth.modeis"password").
Full server guide: Linux Server. Step-by-step VPS example: exe.dev.
Node capabilities
With the bundled Linux Node plugin, the CLI gains openclaw node service device capabilities and does not need the desktop app. A command is advertised to the Gateway only when its capability is enabled and the required local tool is present.
| Capability | Default | Requirement |
|---|---|---|
Desktop notifications (system.notify) | On | notify-send from libnotify and a desktop notification session |
Camera photos and clips (camera.*) | Off | FFmpeg, V4L2 camera access, and PulseAudio or PipeWire for clip audio |
Location (location.get) | Off | GeoClue2 and its where-am-i demo |
Set up the plugin in openclaw.json:
{
plugins: {
entries: {
"linux-node": {
config: {
notify: { enabled: true },
camera: { enabled: true },
location: { enabled: true },
},
},
},
},
}
Restart the node service once these settings change. Availability is decided once per process, and the node advertisement is rebuilt on restart.
The Gateway approves the node's command and capability surface independently of device pairing. On first start, or after you enable more capabilities, approve the pending surface:
openclaw nodes pending
openclaw nodes approve <requestId>
A node may be connected and device-paired while its effective caps and commands stay empty until this approval is done.
Camera devices need to be readable by the service user, usually via the video group. When includeAudio is true, camera clips draw on the default PulseAudio or PipeWire source; microphone audio is available only as that clip track and not as a standalone command. For location, the host's GeoClue policy must permit the node-service user.
camera.snap and camera.clip additionally need the Gateway armed explicitly through gateway.nodes.commands.allow. Payloads, limits, and errors are documented in Camera capture and Location command.
Retired Linux Canvas
The Linux Canvas bridge that shipped with the bundle, along with its desktop Canvas window, is gone.
For inline widgets inside the Control UI, use show_widget.
The macOS widget panel is separate, render-only, and needs a connected
Mac. Neither widget surface brings back the former Linux Canvas bridge or the
A2UI push commands it provided.
Install
- Getting Started
- Install & updates
- Optional: Bun package workflow, Nix, Docker
Gateway service (systemd)
Where a Linux host has no supported service manager, start the Gateway in the
foreground or under a supervisor of your own, rc.d for example. openclaw gateway status --deep reports no supported service manager detected and flags any
leftover service unit as stale. That recorded unit has no bearing on the status
check's configuration or port. Updates still proceed, with a service warning; once the update finishes, restart the Gateway you launched by hand. A user session bus that is unavailable on a systemd host stays a distinct service-access diagnostic.
Install using one of these:
openclaw onboard --install-daemon
openclaw gateway install
openclaw configure # select "Gateway service" when prompted
Repair or migrate an install you already have:
openclaw doctor
By default, openclaw gateway install renders a systemd user unit. Complete service guidance, covering the system-level unit variant for shared or always-on hosts, is in the Gateway runbook.
Managed units escape literal paths on their own. In a unit you write yourself, never wrap WorkingDirectory= or EnvironmentFile= paths in shell quotes, even if they contain spaces. Give every absolute path its own EnvironmentFile= directive; systemd disregards relative paths. A literal percent sign is written as %%.
EnvironmentFile= accepts glob patterns too, so prefix literal glob characters with a backslash. Working-directory paths in managed units must not end in spaces or tabs: systemd 255 drops that trailing whitespace at process start. Rather than risk running in a different directory, OpenClaw rejects such paths; pick a path with no trailing whitespace.
Hand-write a unit only when your setup is custom. Minimal user-unit example
(~/.config/systemd/user/openclaw-gateway[-<profile>].service):
[Unit]
Description=OpenClaw Gateway (profile: <profile>)
After=network-online.target
Wants=network-online.target
StartLimitBurst=10
StartLimitIntervalSec=300
[Service]
ExecStart=/usr/local/bin/openclaw gateway --port 18789
Restart=always
RestartSec=5
RestartPreventExitStatus=78
TimeoutStopSec=330
TimeoutStartSec=30
SuccessExitStatus=0 143
OOMPolicy=continue
KillMode=mixed
[Install]
WantedBy=default.target
Units written by hand do not inherit the adaptive heap sizing that openclaw gateway install writes for managed Gateway services. Prefer the managed installer, or, after allowing for native-memory headroom, set an explicit heap limit in your custom supervisor.
TimeoutStopSec=330 accounts for the Gateway's five-minute cooperative drain and the teardown reserve. To view the current managed unit body, run systemctl --user cat openclaw-gateway.service (or systemctl --user cat openclaw-gateway-<profile>.service for a named profile).
A host shutdown also stops the enclosing user@<uid>.service. Its timeout may cut the Gateway's own drain short; on Ubuntu 24.04, /usr/lib/systemd/system/user@.service.d/timeout.conf sets a five-second limit. Doctor and Gateway status warn whenever the effective user-manager timeout is under 90 seconds. That warning neither triggers a Gateway reinstall nor alters host policy.
Inspect it with systemctl show user@$(id -u).service -p TimeoutStopUSec. For more time, create /etc/systemd/system/user@<uid>.service.d/zz-openclaw-stop-timeout.conf:
[Service]
TimeoutStopSec=90
To preserve the full Gateway drain, use 330, accepting that host shutdown may take longer. The drop-in name sorts after Ubuntu's timeout.conf; run sudo systemctl daemon-reload and check the effective value once more.
Enable it:
systemctl --user enable --now openclaw-gateway[-<profile>].service
Memory pressure and OOM kills
On Linux, when a host, VM, or container cgroup exhausts memory, the kernel chooses an OOM victim. The Gateway makes a poor victim: it holds long-lived sessions and channel connections. OpenClaw therefore biases things so that transient child processes are killed first where possible.
For eligible Linux child spawns, OpenClaw wraps the command in a short /bin/sh shim. The shim tries to raise the child's own oom_score_adj to 1000, then execs the real command. No privileges are needed: any process may raise its own OOM score.
The small spawn broker and service-child anchor skip that extra shell exec: around the native spawn they raise their own score temporarily, then restore it. Before the child can execute or fork descendants, it inherits 1000. Should the helper fail to adjust its score, the shim is used instead. Direct launches and PTYs keep the shim, so the Gateway's own score never has to change.
Child process surfaces covered:
- Command children managed by the supervisor
- PTY shell children
- MCP stdio server children
- Managed local model and embedding service children
- Browser/Chrome processes launched by OpenClaw (through the plugin SDK process runtime)
Sandbox backend transports receive neither this wrapper: they keep their prepared environment and inherited OOM score. Resource policy for workloads is the sandbox backend's job; ordinary host commands and PTYs keep the child-first bias.
The wrapper exists on Linux only and is skipped if /bin/sh is unavailable, or if the child env sets OPENCLAW_CHILD_OOM_SCORE_ADJ to 0, false, no, or
off.
Reserve this opt-out for controlled diagnosis: it drops child-first OOM protection, which makes the Gateway more likely to be chosen as victim under real memory pressure.
When their effective environment defines SHELLOPTS, BASHOPTS, a BASH_FUNC_* key, or a reserved OC_INTERNAL_OOM_EXEC_{BASH_ENV,ENV,CDPATH,PS4} carrier, managed local model and embedding services fall back to direct spawn. Exact environment fidelity and shell startup safety win in those cases, so OpenClaw leaves oom_score_adj alone; check the child's effective value with the verification below.
Check a child process:
cat /proc/<child-pid>/oom_score_adj
When the write goes through, 1000 is the value you should see for covered children. Should /proc be missing or not writable, the child runs anyway, just without the OOM bias. The Gateway process keeps its usual score (typically 0).
With OOMPolicy=continue set on the systemd unit, the Gateway service stays up when the OOM killer picks a transient child, rather than the whole unit being marked failed and every channel restarted; the child or session that failed reports its own error.
Normal memory tuning is still needed. When a VPS or container keeps killing children, increase the memory limit, lower concurrency, or apply tighter resource controls (systemd MemoryMax=, container memory limits).