Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions docs/inference/use-local-inference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,26 @@ The onboard wizard detects Ollama automatically when it is installed or running
If Ollama is installed but not running, NemoClaw starts it for you.
On macOS and Linux, the wizard can also offer to install Ollama when it is not present.
On WSL, the wizard can use, start, restart, or install Ollama on the Windows host through PowerShell interop.
On Debian and Ubuntu, the native Linux install path checks for `zstd` before it runs the Ollama installer.
If `zstd` is missing, NemoClaw installs it with `apt-get` and explains the sudo prompt before continuing.
On non-apt Linux distributions, install `zstd` first, then rerun onboarding.

#### Linux Install Modes

On native Linux, the install path picks between a system install (under `/usr/local`, via the official `https://ollama.com/install.sh`) and a sudo-free user-local install (under `${HOME}/.local`).
NemoClaw selects the mode automatically:

- Running as root or with passwordless sudo (`sudo -n true` returns 0) selects the system install.
- A non-interactive run (`NEMOCLAW_NON_INTERACTIVE=1` or no TTY on stdin) without passwordless sudo selects the user-local install.
This is the path that lets headless hosts complete onboarding without prompting for a sudo password.
- An interactive shell without passwordless sudo selects the system install and lets the official installer prompt for the password as usual.

Override the detection with `NEMOCLAW_OLLAMA_INSTALL_MODE=system` or `NEMOCLAW_OLLAMA_INSTALL_MODE=user`.

The user-local install replicates only the binary extraction step of the official installer.
It downloads the release tarball, extracts it to `${HOME}/.local`, and launches `${HOME}/.local/bin/ollama serve` once.
It does not configure a systemd service, does not create the `ollama` system user, and does not install CUDA drivers, so the daemon must be relaunched manually after a reboot.
NemoClaw also prints a one-line `PATH` hint if `${HOME}/.local/bin` is not already on your `PATH`; you can add `export PATH="${HOME}/.local/bin:$PATH"` to your shell profile to invoke `ollama` directly.

Both modes rely on `zstd` for archive extraction. On Debian and Ubuntu, the system path uses `sudo apt-get` to install `zstd` automatically and explains the prompt before continuing.
The user-local path cannot bootstrap system packages without elevation, so if `zstd` is missing it prints per-distro install hints and exits — install `zstd` manually, then rerun onboarding.

Run the onboard wizard.

Expand Down
1 change: 1 addition & 0 deletions docs/reference/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,7 @@ Set them before running `nemoclaw onboard`.
| `NEMOCLAW_REASONING` | `true` or `false` | Overrides the model's reasoning-mode flag in the built OpenClaw config. |
| `NEMOCLAW_AGENT_HEARTBEAT_EVERY` | duration with `s`, `m`, or `h` suffix (for example `30m`, `1h`, or `0m`) | Overrides `agents.defaults.heartbeat.every` in the built OpenClaw config. Set `0m` to disable periodic agent turns. |
| `NEMOCLAW_OLLAMA_REQUIRE_TOOLS` | `0` to disable, anything else to keep the default | When set to `0`, skips the Ollama tool-calling capability check during local-inference onboarding. |
| `NEMOCLAW_OLLAMA_INSTALL_MODE` | `system`, `user`, or empty/unset | Pins the Linux Ollama install location. `system` runs the official `https://ollama.com/install.sh` (sudo, writes to `/usr/local`, configures systemd). `user` extracts the release tarball to `${HOME}/.local` without sudo and launches the daemon manually (no systemd; manual restart after reboot). Empty/unset auto-detects: root or passwordless `sudo` selects `system`; a non-interactive run without passwordless `sudo` selects `user`; an interactive shell falls back to `system` so the official installer can prompt for the password. Any other value is rejected. |
| `NEMOCLAW_PROXY_HOST` | hostname or IP | Overrides the sandbox-side outbound HTTP proxy host. Defaults to `10.200.0.1`. |
| `NEMOCLAW_PROXY_PORT` | integer port | Overrides the sandbox-side outbound HTTP proxy port. Defaults to `3128`. |
| `NEMOCLAW_OPENSHELL_BIN` | path | Overrides the `openshell` binary the CLI invokes. Defaults to `openshell` (resolved via `PATH`). |
Expand Down
54 changes: 7 additions & 47 deletions src/lib/onboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ const {
const { cleanupTempDir }: typeof import("./onboard/temp-files") = require("./onboard/temp-files");
const { stopStaleDashboardListenersForSandbox } = require("./onboard/stale-gateway-cleanup");
const {
ensureManagedOllamaLoopbackSystemdOverride,
ensureOllamaLoopbackSystemdOverride,
}: typeof import("./onboard/ollama-systemd") = require("./onboard/ollama-systemd");
const { bestEffortForwardStop } = require("./onboard/forward-cleanup");
Expand Down Expand Up @@ -103,6 +102,9 @@ const { buildVllmMenuEntries }: typeof import("./onboard/vllm-menu") = require("
const {
detectWindowsHostOllama,
}: typeof import("./onboard/windows-host-ollama") = require("./onboard/windows-host-ollama");
const {
installOllamaOnLinux,
}: typeof import("./onboard/install-ollama-linux") = require("./onboard/install-ollama-linux");
const crypto = require("node:crypto");
const fs = require("fs");
const os = require("os");
Expand Down Expand Up @@ -1315,20 +1317,6 @@ function hostCommandExists(commandName: string): boolean {
});
}

function ensureOllamaLinuxExtractionDependencies(): void {
if (hostCommandExists("zstd")) return;
console.log(
" The Ollama Linux installer requires zstd for archive extraction. " +
"The next step uses sudo to install zstd; you may be prompted for your password.",
);
runShell(`if ! command -v apt-get >/dev/null 2>&1; then
echo "ERROR: Ollama requires zstd for extraction, and only apt-based Linux is supported here." >&2
echo "Install zstd manually (for example, sudo dnf install zstd or sudo pacman -S zstd), then rerun ${cliName()} onboard." >&2
exit 1
fi
sudo apt-get update -qq && sudo apt-get install -y -qq --no-install-recommends zstd`);
}

function captureProcessArgs(pid: number): string {
return runCapture(["ps", "-p", String(pid), "-o", "args="], {
ignoreError: true,
Expand Down Expand Up @@ -5292,38 +5280,10 @@ async function setupNim(
continue selectionLoop;
}
} else {
ensureOllamaLinuxExtractionDependencies();
console.log(
" The Ollama installer creates a system user, a systemd service, and writes to /usr/local. " +
"It uses sudo, may ask for your password, and can take a few minutes; installer output will stream below.",
);
runShell("set -o pipefail; curl -fsSL https://ollama.com/install.sh | sh", { stdio: "inherit" });
// Give the just-started ollama.service a moment to bind port
// 11434 before we probe or apply the systemd drop-in override.
sleepSeconds(2);
// Linux native + systemd: force a loopback-only OLLAMA_HOST drop-in
// and let systemd own the daemon (avoids racing the installer's
// daemon with our own `ollama serve`). This also repairs older
// NemoClaw-created overrides that exposed raw Ollama on all interfaces.
// WSL and non-systemd Linux fall back to a manual loopback launch.
const overrideState = ensureManagedOllamaLoopbackSystemdOverride({ isNonInteractive });
if (overrideState === "failed") {
console.error(
" Ollama systemd restart did not recover after applying the loopback override.",
);
process.exit(1);
}
// Fall back to manual start only when systemd is unavailable.
if (overrideState === "not-applicable" && !findReachableOllamaHost()) {
console.log(" Starting Ollama...");
runShell(`OLLAMA_HOST=127.0.0.1:${OLLAMA_PORT} ollama serve > /dev/null 2>&1 &`, {
ignoreError: true,
});
if (!waitForHttp(`http://127.0.0.1:${OLLAMA_PORT}/`, 10)) {
console.error(` Ollama did not become ready on :${OLLAMA_PORT} within timeout.`);
if (isNonInteractive()) process.exit(1);
continue selectionLoop;
}
const installResult = installOllamaOnLinux({ isNonInteractive });
if (!installResult.ok) {
if (isNonInteractive()) process.exit(1);
continue selectionLoop;
}
}
if (shouldFrontOllamaWithProxy()) {
Expand Down
Loading
Loading