docs/book/src/hardware/raspberry-pi-setup.md
This guide covers installing and running ZeroClaw on Raspberry Pi.
The runtime is small enough to run comfortably on any Pi. The only constraint is building from source on the device: Rust's linker is memory-hungry (fat LTO can OOM a low-RAM board), so the on-device build path needs swap and a lighter profile. Most users should take the pre-built binary and skip all of that.
Any Pi that can run a 64-bit (aarch64) or 32-bit (armv7) Raspberry Pi OS runs the pre-built binary; there is no meaningful memory floor for the runtime. The prebuilt Pi binaries come from these release targets (64-bit aarch64 for 64-bit Raspberry Pi OS, 32-bit armv7/arm for 32-bit OS):
{{#include ../_snippets/hardware-pi-targets.md}}
Fastest path. No compiler, no swap, no OOM risk.
{{#include ../_snippets/install.md:linux}}
The script auto-detects your architecture (aarch64, armv7, or armv6) and installs the matching release binary into $CARGO_HOME/bin/zeroclaw (defaulting to ~/.cargo/bin/zeroclaw). Make sure that directory is on your PATH.
When the script builds from source instead of taking a prebuilt binary, it also adapts the build to the board's available memory:
{{#include ../_snippets/hardware-lowmem-lto.md}}
Pick the matching tarball from the latest release:
<div class="os-tabs-src"># 64-bit (Pi 4/5 with 64-bit Raspberry Pi OS)
curl -LO https://github.com/zeroclaw-labs/zeroclaw/releases/latest/download/zeroclaw-aarch64-unknown-linux-gnu.tar.gz
tar xzf zeroclaw-aarch64-unknown-linux-gnu.tar.gz
sudo install -m 0755 zeroclaw /usr/local/bin/
# 32-bit (Pi Zero 2 W, older Pi 3 with 32-bit OS)
curl -LO https://github.com/zeroclaw-labs/zeroclaw/releases/latest/download/zeroclaw-armv7-unknown-linux-gnueabihf.tar.gz
tar xzf zeroclaw-armv7-unknown-linux-gnueabihf.tar.gz
sudo install -m 0755 zeroclaw /usr/local/bin/
uname -m
# aarch64 → 64-bit (use the aarch64-unknown-linux-gnu binary)
# armv7l → 32-bit (use the armv7-unknown-linux-gnueabihf binary)
# armv6l → Pi 1 / Zero / Zero W (use the arm-unknown-linux-gnueabihf binary)
If you already have a beefier machine, cross-compiling is faster than building on the Pi.
<div class="os-tabs-src"># Install the cross-compilation target
rustup target add aarch64-unknown-linux-gnu
# Install a Linux GNU cross-toolchain — same pattern used by the Arduino Uno Q guide
brew tap messense/macos-cross-toolchains
brew install aarch64-unknown-linux-gnu
# Build
CC_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-gcc \
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-unknown-linux-gnu-gcc \
cargo build --release --target aarch64-unknown-linux-gnu
# Copy to your Pi
scp target/aarch64-unknown-linux-gnu/release/zeroclaw pi@raspberrypi:~/
Note: earlier drafts of this guide suggested
aarch64-elf-gccfrom Homebrew. That toolchain produces bare-metal ELF binaries and links against newlib, not glibc. It will not produce a working Raspberry Pi OS binary. Use themessense/macos-cross-toolchainstap above (a real Linux GNU/glibc toolchain), or fall back to Option 3 (build on the Pi).
# Install cross-compilation toolchain
sudo apt-get install -y gcc-aarch64-linux-gnu
# Add target
rustup target add aarch64-unknown-linux-gnu
# Configure linker
# [target.aarch64-unknown-linux-gnu]
# linker = "aarch64-linux-gnu-gcc"
# Build
cargo build --release --target aarch64-unknown-linux-gnu
# Copy to Pi
scp target/aarch64-unknown-linux-gnu/release/zeroclaw pi@raspberrypi:~/
The agent compiling itself on the device. Works on any Pi with swap and the right build profile; slower on lower-RAM boards.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
Fat LTO peaks during the final link; without swap, a low-RAM board OOM-kills mid-link.
<div class="os-tabs-src"># Create a 4 GB swap file
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Verify
free -h
# Make persistent across reboots
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Pick a profile by available RAM. release is fat LTO (best binary, heaviest link); release-fast raises codegen-units for a lighter link; ci uses thin LTO for the lowest-memory link. (install.sh picks this automatically; see Using the install script.)
git clone https://github.com/zeroclaw-labs/zeroclaw.git
cd zeroclaw
cargo build --release # higher-RAM board
cargo build --profile release-fast # mid-RAM board
cargo build --profile ci # low-RAM / constrained board
# Install the binary you built:
sudo install -m 0755 target/release/zeroclaw /usr/local/bin/
# (or target/release-fast/zeroclaw, or target/ci/zeroclaw)
To drive Pi GPIO from skills, build with the relevant peripherals feature flag. Most agent workloads don't need it; see Peripherals design.
On a memory-constrained Pi, container runtime choice matters: everything you stack alongside ZeroClaw competes for the same fixed pool, so memory not spent on container infrastructure is memory the agent gets.
Why Podman over Docker on a Pi:
.container unit files systemd manages directly, with no separate docker.service or logging layer.dockerd resident; Podman does not, freeing the largest single chunk of memory without losing isolation.The trade-off: Podman's rootless network (slirp4netns/pasta) is slower than Docker's bridge. For ZeroClaw's "one or two long-running agent containers" pattern that's negligible, and the daemon savings dominate on constrained hardware.
sudo apt-get install -y podman
# Optional: shorter aliases — many docker-compose flows just work with podman-compose
sudo apt-get install -y podman-compose
The published OCI image works under Podman without modification:
<div class="os-tabs-src">podman pull ghcr.io/zeroclaw-labs/zeroclaw:latest
podman run --rm -d \
--name zeroclaw \
-p 42617:42617 \
-v ~/.zeroclaw:/root/.zeroclaw \
ghcr.io/zeroclaw-labs/zeroclaw:latest \
daemon --host 0.0.0.0 --port 42617
Bind gotcha: ZeroClaw defaults to
127.0.0.1for the gateway. Inside a container that means the gateway is unreachable from the host. Always pass--host 0.0.0.0(or setZEROCLAW_BIND=0.0.0.0) when running in a container.
Drop a .container file in /etc/containers/systemd/ (system) or ~/.config/containers/systemd/ (rootless user):
# ~/.config/containers/systemd/zeroclaw.container
[Unit]
Description=ZeroClaw gateway
After=network-online.target
Wants=network-online.target
[Container]
Image=ghcr.io/zeroclaw-labs/zeroclaw:latest
ContainerName=zeroclaw
PublishPort=42617:42617
Environment=ZEROCLAW_BIND=0.0.0.0
Exec=daemon --host 0.0.0.0 --port 42617
Volume=zeroclaw-data:/root/.zeroclaw
[Service]
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target default.target
systemctl --user daemon-reload
systemctl --user start zeroclaw.service
For rootless setups, also run loginctl enable-linger $USER so the service starts before you log in.
zeroclaw quickstart
This walks you through provider auth, gateway config, and creates your ZeroClaw config.
zeroclaw doctor
zeroclaw agent -a assistant -m "what's 2+2?"
# Install and start the systemd user service
zeroclaw service install
systemctl --user enable --now zeroclaw
# So it survives logout / reboot:
loginctl enable-linger $USER
For dev / debugging:
<div class="os-tabs-src">zeroclaw daemon --host 0.0.0.0 --port 42617
ZeroClaw can connect to chat platforms (Matrix, Mattermost, Discord, Telegram, etc.). See Channels → Overview. Most channel transports work fine on a Pi; the heaviest is the WebRTC stack used by some voice channels, which can spike CPU during call setup.
If you want skills to drive GPIO pins (LEDs, buttons, sensors, etc.):
Add your user to the gpio group:
sudo usermod -aG gpio $USER
# Log out and back in for the group change to take effect
Use the peripherals crate's GPIO bindings from your skills. See Hardware → Peripherals design for the abstraction model.
release-fast or ci), or use the pre-built binary / cross-compile.uname -m and grab the matching binary (aarch64 = 64-bit, armv7l = 32-bit).gpio group; sudo usermod -aG gpio $USER, then re-login.loginctl enable-linger $USER so the user service survives logout.127.0.0.1; pass --host 0.0.0.0 (or ZEROCLAW_BIND=0.0.0.0).sudo systemctl set-default multi-user.target.export CARGO_TARGET_DIR=/tmp/zeroclaw-target.clk_ignore_unused isn't on the kernel cmdline if you use a custom image; it inhibits clock gating and raises idle power. Stock Raspberry Pi OS doesn't set it.