Skip to main content

Command Palette

Search for a command to run...

Building a Personal AI Assistant on a $35 Raspberry Pi 3B

A weekend adventure in stubbornness, swap files, and finding the right tool for the job.

Published
11 min read

There's something deeply satisfying about taking a dusty Raspberry Pi off the shelf and turning it into something that talks back to you. Not in the "blinking LED" way — in the "I'll answer your Telegram messages with Claude Sonnet 4.5 at 2 AM" way.

This is the story of how I set up a 24/7 personal AI assistant on a Raspberry Pi 3B with 906 MB of RAM. It involved three different software projects, one spectacular out-of-memory crash loop, a failed WhatsApp integration, and a tiny Go binary that saved everything.

The Hardware

Let's start with what we're working with. The Raspberry Pi 3B is not a powerhouse. It is, by modern standards, a rounding error:

Spec

Value

CPU

ARM Cortex-A53, 4 cores @ 1.2 GHz

RAM

906 MB

Storage

58 GB SD card

Network

Wi-Fi (wlan0)

OS

Raspberry Pi OS (Debian Trixie), Linux 6.12.47, aarch64

Desktop

Wayland (labwc + wf-panel-pi)

906 megabytes. Not gigabytes. Megabytes. Keep that number in your head — it becomes the villain of this story.

Act I: Finding the Pi

The Pi had been sitting on my desk, powered off, half-forgotten. Step one was figuring out if it was even alive on the network.

A quick ARP scan from my Mac revealed four devices on the local Fritz!Box network:

192.168.178.1   — fritz.box (router)
192.168.178.55  — unknown device
192.168.178.72  — abhinav-raspberrypi.fritz.box
192.168.178.80  — my Mac

There it was. 192.168.178.72. I tried SSH and got No route to host. Powered the Pi on, tried again — Host key verification failed. Cleared the old key, connected with password auth, and was greeted by a fresh boot:

abhinav-raspberrypi
Linux abhinav-raspberrypi 6.12.47+rpt-rpi-v8
up 3 min, 3 users, load average: 1.33, 1.59, 0.72

We were in.

Act II: The OpenClaw Dream

The plan was simple: install OpenClaw, the open-source personal AI assistant that had taken GitHub by storm — exploding from 9,000 to 60,000 stars in a matter of days. OpenClaw promised a "24/7 Jarvis" experience: a self-hosted AI agent that connects to WhatsApp, Telegram, Slack, Discord, and more. It could read files, run shell commands, execute code in a sandbox. The dream.

I started by reading the official docs, the DigitalOcean overview, and the OpenRouter integration guide. The installation looked straightforward:

# Step 1: Node.js 22 (required)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -
sudo apt install nodejs -y

# Step 2: Build tools (for native modules)
sudo apt install -y build-essential python3 libtool autoconf automake libopus-dev

# Step 3: OpenClaw itself
sudo npm install -g openclaw@latest

The first two steps went fine. The third one didn't.

The @discordjs/opus Wall

OpenClaw depends on @discordjs/opus, a native Node.js addon for audio encoding. It needs to compile C++ code against the Opus library during installation. On aarch64 Raspberry Pi OS, this compilation failed spectacularly:

node-pre-gyp ERR! build error
Failed to execute node-gyp build

No prebuilt binary existed for node-v127-napi-v3-linux-arm64-glibc-2.41. The fallback source compilation also failed. I tried --omit=optional, but it wasn't an optional dependency. I tried --ignore-scripts — and that actually worked:

sudo npm install -g --ignore-scripts openclaw@latest

OpenClaw installed. Version 2026.2.22-2. The openclaw binary was in PATH. I could run openclaw doctor. Things were looking up.

Configuring with OpenRouter

I configured OpenClaw to use OpenRouter as the LLM provider, pointing at anthropic/claude-sonnet-4-5. The config lives at ~/.openclaw/openclaw.json, and after some trial and error with the schema (the docs say models.default, the validator says agents.defaults.model.primary), I landed on the correct format:

{
  "env": {
    "OPENROUTER_API_KEY": "sk-or-..."
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "openrouter/anthropic/claude-sonnet-4-5"
      }
    }
  },
  "gateway": {
    "mode": "local"
  }
}

openclaw doctor passed with no config errors. I installed the systemd daemon. Everything was ready.

The OOM Apocalypse

Then I started the gateway.

And waited. And waited. The port never bound. After five minutes of checking ss -tlnp | grep 18789 and getting nothing, I ran the gateway in the foreground to see what was actually happening:

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

[18674:0x7fa8020000] 79382 ms: Mark-Compact 450.7 (463.4) -> 449.3 (464.6) MB

450 megabytes of heap. On a machine with 906 MB total. The Node.js V8 engine was trying to allocate more memory than physically existed on the board.

I tried everything:

  • Set NODE_OPTIONS=--max-old-space-size=512 — still OOM

  • Set NODE_OPTIONS=--max-old-space-size=768 — still OOM

  • Created a 2 GB swap file on the SD card — still OOM

  • Removed the heap limit entirely, hoping swap would save us — the process was killed by the OOM killer after consuming 3+ minutes of CPU time

The Raspberry Pi 3B simply cannot run the OpenClaw gateway. Its Node.js runtime, with all the messaging channel adapters, skill system, and agent framework loaded, requires more memory than the hardware can provide. Even with swap. Even with prayer.

Act III: PicoClaw to the Rescue

I went searching for alternatives. The Raspberry Pi setup guide by zenvanriel and the official Raspberry Pi blog post both acknowledged the memory problem and pointed to two lightweight alternatives:

  1. PicoClaw — a slimmed-down Go agent for minimal hardware

  2. ZeroClaw — a pure Rust rewrite running in under 5 MB RAM

I chose PicoClaw. The installation guide on vibestacklab was excellent, and the project had prebuilt ARM64 binaries on GitHub.

Installation was almost comically simple compared to the OpenClaw ordeal:

wget https://github.com/sipeed/picoclaw/releases/download/v0.1.2/picoclaw_Linux_arm64.tar.gz
tar xzf picoclaw_Linux_arm64.tar.gz
sudo mv picoclaw /usr/local/bin/picoclaw

5.5 MB binary. No Node.js. No npm. No native compilation. No @discordjs/opus.

I ran picoclaw onboard to generate the default config, then configured it with OpenRouter and Telegram:

{
  "agents": {
    "defaults": {
      "provider": "openrouter",
      "model": "anthropic/claude-sonnet-4-5"
    }
  },
  "channels": {
    "telegram": {
      "enabled": true,
      "token": "<bot-token-from-BotFather>"
    }
  },
  "providers": {
    "openrouter": {
      "api_key": "sk-or-..."
    }
  }
}

The moment of truth:

$ picoclaw agent -m "Hello!"

🦞 Hello! I'm picoclaw, your lightweight AI assistant, ready to help you
   with tasks, answer questions, and make your day a bit easier!

It worked. In under 2 seconds. On a Raspberry Pi 3B.

I started the gateway:

✓ Channels enabled: [telegram]
✓ Gateway started on 0.0.0.0:18790
✓ Cron service started
✓ Heartbeat service started
[INFO] telegram: Telegram bot connected {username=iamabhinav_bot}

Total system memory used: ~230 MB. Compare that to OpenClaw's 450+ MB heap allocation that crashed the machine. PicoClaw was using half the resources and actually running.

I set it up as a systemd service for 24/7 operation:

[Unit]
Description=PicoClaw AI Assistant
After=network-online.target

[Service]
ExecStart=/usr/local/bin/picoclaw gateway
Restart=always
RestartSec=5

[Install]
WantedBy=default.target

The bot survived a reboot, auto-started, and was responding to Telegram messages within seconds of the Pi coming online.

Act IV: The WhatsApp Saga (A Cautionary Tale)

Emboldened by the Telegram success, I decided to connect WhatsApp. PicoClaw's WhatsApp channel uses a WebSocket bridge architecture — it doesn't connect to WhatsApp directly like Telegram. You need a separate bridge service running on ws://localhost:3001.

Attempt 1: whatsapp-ws

I found whatsapp-ws by monobilisim, a Go-based WhatsApp WebSocket bridge built on the whatsmeow library. I installed Go 1.24, cloned the repo, built it. It started up fine.

Then WhatsApp rejected the connection:

[Client ERROR] Client outdated (405) connect failure (client version: 2.2333.11)
[Main INFO] QR channel result: err-client-outdated

The whatsmeow version bundled with whatsapp-ws was ancient. WhatsApp's servers refused to talk to it.

Attempt 2: Updating the dependency

I tried updating the whatsmeow dependency in whatsapp-ws:

go get go.mau.fi/whatsmeow@latest
go mod tidy
go build

The API had changed between versions. Fields renamed (Url -> URL, FileEncSha256 -> FileEncSHA256), function signatures changed (new context.Context parameters everywhere). The build produced a wall of compilation errors.

Attempt 3: Writing a custom bridge

I wrote a minimal WhatsApp WebSocket bridge from scratch using the latest whatsmeow library — about 120 lines of Go. It compiled clean, started up, and produced a QR code.

WhatsApp said: "Can't scan this QR code."

The terminal rendering of the QR code — despite being generated by the same qrterminal library that every WhatsApp bridge uses — wasn't being recognized by WhatsApp's scanner. The QR code was valid (it contained the right data), but the terminal font rendering over SSH made it unscannable.

At this point, I made the wise decision to cut my losses. WhatsApp integration on a headless Pi 3B, through a third-party bridge, over a terminal QR code, was a bridge too far (pun intended). I deleted everything:

systemctl --user stop whatsapp-ws
systemctl --user disable whatsapp-ws
rm -f ~/.config/systemd/user/whatsapp-ws.service
sudo rm -f /usr/local/bin/whatsapp-ws /usr/local/bin/wa-bridge
rm -rf ~/.whatsapp-ws

Telegram alone would do just fine.

Act V: Remote Access with Raspberry Pi Connect

The last piece of the puzzle was remote access. I installed Raspberry Pi Connect (v2.6.1), which was already pre-installed on the Pi but not activated:

rpi-connect on
rpi-connect signin
# Visit the verification URL in a browser

After signing in with a Raspberry Pi ID, the Pi was accessible from anywhere at connect.raspberrypi.com — both screen sharing and remote shell, right in the browser.

For local network access, plain SSH still works:

ssh abhinav@192.168.178.72
ssh abhinav@abhinav-raspberrypi.fritz.box

The Swap File Detail

One thing that bit me: the Pi 3B uses zram (compressed RAM) as its default swap, giving 906 MB of "swap" that's really just compressed memory. I created a real 2 GB swap file on the SD card for additional headroom:

sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

To make it persistent across reboots, I added it to /etc/fstab:

/swapfile none swap sw 0 0

Is more than 2 GB of swap useful on a Pi 3B? Not really. The SD card writes at ~13 MB/s. Heavy swap usage makes the system crawl. The 2 GB is a safety net, not a performance feature. If your workload needs more than 906 MB RAM + 2 GB swap, the answer is a Pi 4 or 5, not more swap.

The Final Setup

Here's what's running 24/7 on the Pi:

Service

What it does

Memory

PicoClaw

AI assistant gateway with Telegram

~230 MB

Raspberry Pi Connect

Remote browser access

Minimal

And the model can be changed at any time:

# Edit ~/.picoclaw/config.json and change the model field
sed -i 's|anthropic/claude-sonnet-4-5|google/gemini-2.5-pro-preview|' ~/.picoclaw/config.json
systemctl --user restart picoclaw

Popular OpenRouter models that work well:

Model

OpenRouter ID

Claude Sonnet 4.5

anthropic/claude-sonnet-4-5

Claude Haiku 4.5

anthropic/claude-haiku-4-5

GPT-4o

openai/gpt-4o

Gemini 2.5 Pro

google/gemini-2.5-pro-preview

DeepSeek V3

deepseek/deepseek-chat

Lessons Learned

1. Read the hardware requirements, then halve your expectations. OpenClaw's docs say "512MB-1GB RAM." In practice, the gateway alone needs 450+ MB of V8 heap. On a 906 MB machine with an OS to run, that's a death sentence.

2. Go binaries are a gift to embedded systems. PicoClaw's 5.5 MB static binary, with no runtime dependencies, no package manager, no compilation step — it just works. This is what "lightweight" actually means.

3. WhatsApp is hostile to bridges. Between outdated client protocols, constantly changing APIs, and QR codes that won't scan over terminal sessions, WhatsApp integration is the hardest messaging channel to self-host. Telegram, by comparison, is a dream: create a bot with @BotFather, paste the token, done.

4. Swap is not RAM. A 2 GB swap file on an SD card writing at 13 MB/s is not a substitute for actual memory. It prevents OOM kills, but the system becomes unusable under heavy swap pressure.

5. The right tool for the right hardware. OpenClaw is brilliant software — on a machine with 2+ GB RAM. PicoClaw exists specifically for the Pi 3B and similar constrained hardware. Knowing when to switch tools is more important than making the wrong tool work.


The Pi is now sitting on my desk, LED blinking quietly, answering my Telegram messages through Claude Sonnet 4.5, 24 hours a day. It cost $35 in hardware and a Sunday afternoon of debugging. Not bad for a personal AI assistant.

Total cost: Raspberry Pi 3B (\(35) + SD card (\)10) + OpenRouter API credits (pay-per-use). No monthly subscriptions. No cloud VMs. Just a tiny computer doing its thing.