Platforms
Where PersonalClaw runs and what to know per platform. The recommended install
paths (uv tool, the bootstrap one-liner, Docker Compose) are the same
everywhere — see Getting started. This page covers only
the platform-specific gotchas.
Support matrix
Section titled “Support matrix”Every row names the mechanism that proves it. No row claims “supported” without one, and the token points at something you can go read or re-run:
CI:<job>— a job in.github/workflows/that runs the suite (or a release smoke) on that platform. Green onmain/nightly is the evidence.checklist:<section>— a documented manual walkthrough; evidence is a recorded run, not a CI job.community— reported working by users; not verified by us.
| Platform | Support | Proof |
|---|---|---|
| Linux x86-64 | first-class | CI:ci/test (ubuntu-24.04) + CI:breadth/ubuntu (ubuntu-24.04) + CI:release/images smoke (linux/amd64) |
| Linux arm64 | first-class | CI:breadth/arm (ubuntu-24.04-arm) + CI:release/images smoke (linux/arm64) |
| macOS Apple silicon | first-class | CI:ci/test-macos (macos-26) + CI:breadth/macos (macos-26) |
| macOS Intel | best-effort | community — no Intel runner in CI; the x86-64 Python/wheel path is the same as Linux x86-64 |
| Windows via WSL2 | supported | checklist:Windows via WSL2 (this page) |
| Windows via Docker Desktop | supported | checklist:Windows via Docker Desktop (this page) — written, not yet executed verbatim; the release runbook’s Windows checklist records the first run |
| Windows native | not supported | — see windows-native-audit |
The arm64 rows became CI-backed with the arm jobs in what is now breadth.yml and the
per-arch release smoke; before that they were aspirational.
The two CI:release/images smoke citations above were, for a while, a proof that had
never run. The step shipped on 2026-08-10 and the last release before that was
v0.1.3 on 2026-07-31, so it had executed zero times — and it invoked the container
command through a login shell, which sources /etc/profile, which overwrites
PATH instead of extending it, so the gateway image’s own ENV PATH was discarded and
personalclaw was not found (exit 127, measured against the published 0.1.3 image).
That is fixed, and the smoke now also requires the version the image reports to be the
version being released (#3562).
Worth stating plainly, because it generalises past this page: a gate that has never
executed is not evidence, however precisely it is cited.
The matrix above is the backend. The desktop shell is narrower: every release
attaches an unsigned Linux x86-64 AppImage and deb (CI:release/desktop-linux smoke)
and an Apple-silicon macOS dmg, ad-hoc signed and not notarized
(CI:release/desktop-mac smoke), and there is no Windows build — see
the desktop guide for the per-OS signing story and the dated
Windows deferral.
The [models] extra, per architecture
Section titled “The [models] extra, per architecture”pip install 'personalclaw[models]' pulls the local-embedding stack. Wheel
availability — not PersonalClaw — is what varies by arch. Read from the committed
uv.lock (the resolver’s own record, so it stays honest as versions move):
| Package | x86-64 | arm64 (macOS) | arm64 (Linux) | Note |
|---|---|---|---|---|
faiss-cpu | ✅ wheel | ✅ macosx_14_0_arm64 | ✅ manylinux_2_28_aarch64 + musllinux_1_2_aarch64 | musllinux wheel means Alpine works too |
torch | ✅ wheel | ✅ macosx_14_0_arm64 | ✅ manylinux_2_28_aarch64 | the lock takes Linux’s CPU build from PyTorch’s CPU index, so the image carries no CUDA; pip takes PyPI’s |
sentence-transformers | ✅ | ✅ | ✅ | py3-none-any — pure Python, arch-independent |
So [models] installs from wheels on every arch we claim — no source build, no
compiler needed. Verify it yourself without an arm box:
python3 - <<'PY'import refor blk in re.findall(r'\[\[package\]\]\nname = "torch"(.*?)(?=\n\[\[package\]\]|\Z)', open("uv.lock").read(), re.S): print([w for w in re.findall(r'([\w.\-%]+\.whl)', blk) if "aarch64" in w or "arm64" in w])PYmacOS arm64: faiss-cpu and torch ship two OpenMP runtimes
Section titled “macOS arm64: faiss-cpu and torch ship two OpenMP runtimes”Both macOS wheels bundle their own copy of LLVM’s OpenMP runtime
(faiss/.dylibs/libomp.dylib, torch/lib/libomp.dylib). Loading both packages is
harmless; initializing the second runtime is not, and faiss initializes its copy
the first time it enters a parallel region — its search, not its add. A process
holding both therefore dies with OMP: Error #15 and SIGABRT on the next episodic
dedup search rather than at import, which is why the symptom never points at the
cause (#3324).
Consequences for how you run it on macOS:
- Core stays torch-free on purpose. Nothing in
personalclawimportstorch,faster_whisperorsentence_transformers; thesentence-transformersandfaster-whisperapps own those dependencies. That is an enforced invariant, not a convention —tests/native_omp_guard.pyfails the test that makestorchresident. - Installing
sentence-transformersdoes not abort, for a reason you should not rely on. It importssklearn, whose__init__runsos.environ.setdefault("KMP_DUPLICATE_LIB_OK", "True")— the workaround LLVM documents as unsafe and able to produce silently wrong results. The pairing is suppressed there, not absent. - An in-process STT model app is the live hazard.
faster_whisperbringstorchwithoutsklearn, so nothing sets that flag; withfaissalso present, the gateway aborts on the next episodic write. Prefer a remote STT provider on macOS until that provider runs out-of-process.
KMP_DUPLICATE_LIB_OK=TRUE is not a supported configuration here: it turns a crash
into undefined behaviour underneath your memory store.
RAM floor on Pi-class boards
Section titled “RAM floor on Pi-class boards”The embedding stack, not the gateway, is what strains small boards. The gateway
itself is light; torch + a loaded embedding model is the heavy part.
- < 2 GB RAM — skip the extra. Install plain
personalclawand use a remote provider for embeddings. Everything except local embedding works unchanged. - 2–4 GB (Pi 4/5 class) —
[models]can work, but add swap before first use; the model load is the spike, not steady state:Prefer a small model, and expect the first ingest to be slow.Terminal window sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfilesudo mkswap /swapfile && sudo swapon /swapfile# persist: echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab - ≥ 4 GB — no special handling.
If you hit an OOM kill during ingest rather than at startup, it is the model load — add swap or drop the extra; it is not a database or gateway problem.
Windows via WSL2
Section titled “Windows via WSL2”Windows has no native build. The supported path is WSL2 (Windows Subsystem for Linux, version 2): a real Linux kernel inside Windows where PersonalClaw runs as an ordinary Linux install. The Windows-side browser reaches the dashboard through WSL2’s automatic localhost forwarding.
If you would rather not run a Linux shell at all, use Windows via Docker Desktop below — Docker Desktop itself uses a WSL2 backend, but you never touch the Linux shell. The rest of this section is for running PersonalClaw directly in WSL2.
1. Install in WSL2
Section titled “1. Install in WSL2”From a WSL2 shell (Ubuntu or any distro), install exactly as on Linux — with
uv, which downloads Python 3.13 if the distro has none:
uv tool install --python 3.13 personalclawpersonalclaw setup # interactive: workspace directory + timezonepersonalclaw gatewaypersonalclaw doctor prints a platform: WSL detected line and tells you
whether the background service will work (see below).
2. Keep your home on ext4, NOT on /mnt/c — this matters
Section titled “2. Keep your home on ext4, NOT on /mnt/c — this matters”Store ~/.personalclaw/ on the WSL ext4 filesystem (i.e. under your Linux
home, /home/<you>), not under /mnt/c (the mounted Windows drive).
The /mnt/c mount crosses the Windows/Linux filesystem boundary (a 9P network
protocol), and small random I/O across it is dramatically slower — often
10-20x. PersonalClaw’s SQLite databases and FTS index do exactly that kind of
I/O, so a home on /mnt/c makes chat history, memory, and search crawl.
Leave PERSONALCLAW_HOME unset (defaults to ~/.personalclaw) or point it at
another ext4 path. Do not set it to a /mnt/c/... path.
3. Opening the dashboard (localhost forwarding + wslview)
Section titled “3. Opening the dashboard (localhost forwarding + wslview)”WSL2 automatically forwards localhost between Windows and the Linux VM, so the
dashboard URL the gateway prints (http://localhost:10000/...) opens directly
in a Windows browser.
Started at a terminal, the gateway prints the URL prominently and then tries to open
it. Inside WSL there is no Linux browser to launch, so PersonalClaw hands the URL to
wslview (from the wslu package),
which opens it in your Windows default browser. Most WSL distros ship wslu; if
wslview is missing, install it (sudo apt install wslu) or just click the URL
the gateway printed. Auto-open never blocks startup — a missing wslview is not
an error. The background service below opens no browser: run personalclaw token
for a sign-in link.
4. Background service needs systemd (opt-in on WSL2)
Section titled “4. Background service needs systemd (opt-in on WSL2)”personalclaw service install registers a systemd unit so the gateway starts on
boot and restarts on failure. WSL2 runs systemd only when you opt in. Enable it
once:
-
Create or edit
/etc/wsl.confinside your distro:[boot]systemd=true -
From Windows (PowerShell or CMD), fully restart the distro so the change takes effect:
Terminal window wsl --shutdownReopen your WSL shell.
personalclaw doctorshould now reportservice: systemd active.
Without systemd the background service will not persist. In that case, either
run the gateway in a foreground shell (personalclaw gateway) whenever you need
it, or start it on Windows login via Task Scheduler with a
wsl -d <distro> -- personalclaw gateway action.
Windows via Docker Desktop
Section titled “Windows via Docker Desktop”The no-Linux-shell path: Docker Desktop runs the published multi-arch images, so you never install Python or open a WSL prompt. You do need Docker Desktop with its WSL2 backend (its default; the legacy Hyper-V backend is not tested).
1. Get the compose file and a .env
Section titled “1. Get the compose file and a .env”From a checkout:
git clone https://github.com/PersonalClaw/PersonalClaw.gitcd PersonalClawcopy .env.example .envOr with just the compose file, no checkout at all:
curl.exe -fsSLO https://raw.githubusercontent.com/PersonalClaw/PersonalClaw/main/deploy/compose/compose.yaml# optional: put a .env next to itOpen .env and set any provider keys you use. All of them are optional, and with none
set, first-run setup can still download the small default model. Paths in .env must be
container paths, not Windows paths — the gateway runs inside Linux, so
C:\Users\you\... means nothing to it. Leave PERSONALCLAW_HOME alone; compose
already sets it to /data, backed by a named volume.
Where the
.envgoes:compose.yamldeclares two candidate locations and marks bothrequired: false—./.envbeside the compose file (the standalone layout) and../../.env, the repo root (the from-a-checkout layout). Whichever exists is loaded; if the repo root also has one it wins, since a laterenv_fileentry overrides an earlier one. A relativeenv_filepath always resolves from the compose file’s parent directory, never from your shell’s cwd.Before v0.2 the file declared a bare
env_file: ../../.env, andrequireddefaults to true in the Compose spec — so a copy ofcompose.yamlon its own did not “silently skip” your keys, it failed to start at all withenv file /path/.env not found. That is fixed; both locations are now optional.
2. Start it
Section titled “2. Start it”docker compose -f deploy/compose/compose.yaml up -dFirst run pulls both images (personalclaw-gateway, personalclaw-web). Pin a
release instead of latest by setting PERSONALCLAW_IMAGE_TAG in .env.
3. Open the dashboard
Section titled “3. Open the dashboard”Ports are published on loopback only (127.0.0.1), which is what you want on
a laptop:
| URL | What |
|---|---|
https://localhost:3443 | the dashboard (HTTP/2; SSE + WebSocket streams) |
http://localhost:3000 | 308-redirects to the HTTPS port above |
http://localhost:10000 | the gateway API directly |
The HTTPS certificate is self-signed out of the box, so the browser shows a
warning on first visit — expected; click through. (Mount a real cert over
/etc/nginx/certs/personalclaw.{crt,key} to replace it.)
Docker Desktop forwards published ports to Windows localhost automatically, so
no port-proxy or firewall rule is needed for loopback access.
4. Volume semantics — use the named volume, not a bind mount
Section titled “4. Volume semantics — use the named volume, not a bind mount”State lives in the personalclaw_home named volume mounted at /data. Keep
it that way on Windows. A bind mount from an NTFS path (-v C:\...:/data) crosses
the Windows↔Linux filesystem boundary, and PersonalClaw’s SQLite databases do
small random I/O plus file locking across it — which is both much slower and a
known source of locking oddities. The named volume lives inside the WSL2 VM’s
ext4 disk and behaves like native Linux storage.
Useful volume operations:
docker volume inspect compose_personalclaw_home # where it livesdocker compose -f deploy/compose/compose.yaml down # stop, KEEP the volumedocker compose -f deploy/compose/compose.yaml down -v # stop and DELETE statePrefer personalclaw snapshot (run inside the gateway container) over copying
the volume by hand:
docker compose -f deploy/compose/compose.yaml exec personalclaw-gateway personalclaw snapshot5. Updating
Section titled “5. Updating”docker compose -f deploy/compose/compose.yaml pulldocker compose -f deploy/compose/compose.yaml up -dThe named volume survives, so your state carries across the upgrade.
Known limits on this path
Section titled “Known limits on this path”- No
personalclaw service install. Container restart policy replaces it —restart: unless-stoppedalready brings the stack back when Docker Desktop starts. Enable Start Docker Desktop when you log in for boot behaviour. - The desktop shell does not run on Windows (it ships for macOS and Linux — see the desktop guide) and is unrelated to this path.
- Docker Desktop’s Hyper-V backend is untested; use WSL2.