Edge Computing: Building a 24/7 Always-On Raspberry Pi Agent Node
Chapter 37: Edge Computing in Practice — Complete Setup of a Raspberry Pi 24/7 Always-On Agent Node
Overview
The Raspberry Pi is one of the best platforms for an OpenClaw Headless Node. It's compact, low-power, capable of running around the clock, affordable, and equipped with rich GPIO interfaces for sensor integration. This chapter walks you step by step — starting from hardware selection — through configuring a Raspberry Pi as a production-grade Always-On Agent Node: always connected to your Gateway, ready to respond to Agent commands at any time, and capable of autonomous camera capture and sensor data collection.
37.1 Hardware Selection
Recommended Configurations
| Model | RAM | Recommendation | Use Case |
|---|---|---|---|
| Raspberry Pi 5 (4GB) | 4 GB | Top pick | High-frequency command execution, multiple sensors |
| Raspberry Pi 5 (8GB) | 8 GB | Flagship | Local lightweight inference + Node dual purpose |
| Raspberry Pi 4B (4GB) | 4 GB | Recommended | Standard Node scenarios |
| Raspberry Pi 4B (2GB) | 2 GB | Acceptable | Low-load pure command execution |
| Raspberry Pi Zero 2W | 512 MB | Not recommended | Too little RAM; Node.js 22 will struggle |
Recommended: Raspberry Pi 4B 4GB or Raspberry Pi 5 4GB. The Pi 5 offers roughly 2-3x the performance, and its USB 3.0 bandwidth is much more comfortable for camera connectivity.
Required Accessories
- MicroSD card: 32GB+, Class 10 / A1 rated (Samsung EVO series recommended)
- Power supply: Official USB-C power adapter (Pi 4B: 15W / Pi 5: 27W)
- Cooling: Active fan or aluminum heatsink case (essential for 24/7 operation)
- Network: Wired Ethernet preferred (stability > wireless)
- Optional: USB camera (Logitech C920 / Pi Camera Module 3)
Storage Recommendation
For production use, strongly consider using an SSD instead of a MicroSD card (via USB 3.0 adapter):
# Pi 5 also supports a NVMe HAT (M.2 SSD direct connection)
# SSD lifespan is approximately 10x longer than MicroSD,
# with roughly 5x higher read/write speeds
37.2 System Configuration
Operating System Selection
| OS | Advantages | Use Case |
|---|---|---|
| Ubuntu Server 24.04 LTS (ARM64) | Latest packages, matches mainstream documentation | Recommended; first choice for production |
| Raspberry Pi OS Lite (64-bit) | Official optimization, best hardware compatibility | When GPIO/Camera modules are needed |
| Debian 12 Bookworm (ARM64) | Most stable | Minimal-maintenance scenarios |
This chapter uses Ubuntu Server 24.04 LTS ARM64 as the example.
Writing the OS Image
# Use Raspberry Pi Imager (recommended)
# Or use dd (Linux/macOS):
sudo dd if=ubuntu-24.04-preinstalled-server-arm64+raspi.img.xz \
of=/dev/sdX bs=4M status=progress conv=fsync
In Raspberry Pi Imager, configure before writing:
- Set hostname:
openclaw-node-01 - Enable SSH (public key authentication)
- Configure Wi-Fi (if not using wired connection)
- Set timezone: your local timezone
First-Boot Configuration
# SSH into the device
ssh [email protected]
# Update the system
sudo apt update && sudo apt upgrade -y
# Set timezone
sudo timedatectl set-timezone America/New_York
# Configure hostname
sudo hostnamectl set-hostname openclaw-node-01
# Enable and configure firewall (allow SSH)
sudo ufw allow OpenSSH
sudo ufw enable
# Disable unnecessary services (reduce power consumption)
sudo systemctl disable bluetooth
sudo systemctl disable cups
sudo systemctl stop bluetooth cups
Memory Optimization
Edit /boot/firmware/config.txt (Ubuntu) or /boot/config.txt (Pi OS):
# Reduce GPU memory allocation (no graphical interface)
gpu_mem=16
# Enable USB boot (if using SSD)
# program_usb_boot_mode=1
37.3 Installing Node.js 22 LTS
OpenClaw Node requires Node.js 22 LTS (the current LTS release).
Method 1: NodeSource Official Repository (Recommended)
# Install the NodeSource setup script
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
# Install Node.js
sudo apt install -y nodejs
# Verify installation
node --version # v22.x.x
npm --version # 10.x.x
Method 2: nvm (Suitable for Development Machines)
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
# Reload the shell
source ~/.bashrc
# Install Node.js 22
nvm install 22
nvm use 22
nvm alias default 22
Verify the Environment
node --version
# v22.14.0
npm --version
# 10.9.2
# Verify ARM64 architecture
node -e "console.log(process.arch)"
# arm64
37.4 Installing OpenClaw Node
# Install the OpenClaw CLI globally (includes the node subcommand)
sudo npm install -g @openclaw/cli
# Verify installation
openclaw --version
# openclaw 4.2.1
# Initialize Node configuration
openclaw node init
openclaw node init guides you through:
- Enter the Gateway address (e.g.,
192.168.1.100ormygateway.ts.net) - Set the node display name (e.g.,
Pi Node - Living Room) - Choose the capabilities to declare (Headless mode defaults to
system.run) - Generate and save the node ID
The configuration file is saved at ~/.openclaw/node.json by default:
{
"nodeId": "node-a1b2c3d4",
"displayName": "Pi Node - Living Room",
"gateway": {
"host": "192.168.1.100",
"port": 18789,
"tls": false
},
"capabilities": ["system.run", "system.which"],
"reconnect": {
"enabled": true,
"initialDelay": 5,
"maxDelay": 300,
"multiplier": 2
}
}
37.5 Remote Connection Commands
Basic Connection Command
# Run in foreground (for testing)
openclaw node run \
--host 192.168.1.100 \
--port 18789 \
--display-name "Pi Node - Living Room"
# Run with configuration file (recommended)
openclaw node run --config ~/.openclaw/node.json
# Connect to a Gateway at a Tailscale address
openclaw node run \
--host mygateway.ts.net \
--port 18789 \
--display-name "Raspberry Pi 4B" \
--tls
# Specify additional capabilities
openclaw node run \
--host 192.168.1.100 \
--port 18789 \
--display-name "Pi Camera Node" \
--capabilities system.run,system.which
Connection Verification
On a successful connection, the Gateway outputs:
[Gateway] Node connected: Pi Node - Living Room (node-a1b2c3d4)
[Gateway] Capabilities registered: system.run, system.which
[Gateway] Node status: pending (awaiting approval)
Approve on the Gateway host:
openclaw devices list
# ID DISPLAY NAME PLATFORM STATUS
# req-x1y2 Pi Node - Living Room headless pending
openclaw devices approve req-x1y2
# ✓ Device approved: Pi Node - Living Room
37.6 Architecture Diagram: Gateway in the Cloud, Raspberry Pi on-Premises
┌─────────────────────────────────────────────────────┐
│ Cloud (AWS) │
│ │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ OpenClaw │ │ Gateway │ │
│ │ Agent │◄──►│ (Inference + Routing + │ │
│ │ (LLM calls)│ │ WebSocket) port: 18789 │ │
│ └─────────────┘ └──────────┬──────────────────┘ │
│ │ WebSocket (TLS) │
└────────────────────────────────┼────────────────────┘
│
Internet / Tailscale VPN
│
┌────────────────────────────────┼────────────────────┐
│ Local Network │
│ │ │
│ ┌─────────────────────────────▼──────────────────┐ │
│ │ Raspberry Pi 4B / 5 │ │
│ │ │ │
│ │ openclaw node run (role: "node") │ │
│ │ │ │
│ │ Capabilities: │ │
│ │ ✓ system.run ← Shell command execution │ │
│ │ ✓ system.which │ │
│ │ │ │
│ │ Optional extensions: │ │
│ │ ✓ USB camera → camera.snap │ │
│ │ ✓ Temp/humidity sensor (DHT22) → sensor.read │ │
│ │ ✓ GPIO control → gpio.write │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Workflow:
User → Control UI → Gateway → Agent Inference →
Tool Call (system.run) → Route to Raspberry Pi →
Execute command locally → Return result → Agent → User
37.7 systemd Service Configuration for Auto-Start
Create a Dedicated User (Security Best Practice)
# Create a no-shell system user
sudo useradd --system --no-create-home --shell /usr/sbin/nologin openclaw-node
# Create configuration directories
sudo mkdir -p /etc/openclaw /var/log/openclaw
sudo chown openclaw-node:openclaw-node /var/log/openclaw
# Copy configuration file
sudo cp ~/.openclaw/node.json /etc/openclaw/node.json
sudo chown openclaw-node:openclaw-node /etc/openclaw/node.json
sudo chmod 640 /etc/openclaw/node.json
systemd Service Unit File
Create /etc/systemd/system/openclaw-node.service:
[Unit]
Description=OpenClaw Agent Node
Documentation=https://docs.openclaw.ai/nodes
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Type=simple
User=openclaw-node
Group=openclaw-node
# Executable path (confirm with: which openclaw)
ExecStart=/usr/bin/openclaw node run --config /etc/openclaw/node.json
# Working directory
WorkingDirectory=/var/lib/openclaw
# Environment variables (do NOT place secrets here; use EnvironmentFile)
Environment=NODE_ENV=production
Environment=LOG_LEVEL=info
EnvironmentFile=-/etc/openclaw/node.env
# Restart policy
Restart=on-failure
RestartSec=10s
# Log output
StandardOutput=append:/var/log/openclaw/node.log
StandardError=append:/var/log/openclaw/node-error.log
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/var/lib/openclaw /var/log/openclaw /etc/openclaw
AmbientCapabilities=
# Resource limits
LimitNOFILE=65536
MemoryMax=512M
[Install]
WantedBy=multi-user.target
Environment Variable File
Create /etc/openclaw/node.env (mode 600):
# Node-specific environment variables (if needed)
OPENCLAW_NODE_LOG_LEVEL=info
OPENCLAW_NODE_RECONNECT_ENABLED=true
sudo chown root:openclaw-node /etc/openclaw/node.env
sudo chmod 640 /etc/openclaw/node.env
Create the Working Directory
sudo mkdir -p /var/lib/openclaw
sudo chown openclaw-node:openclaw-node /var/lib/openclaw
Enable and Start the Service
# Reload systemd configuration
sudo systemctl daemon-reload
# Enable auto-start on boot
sudo systemctl enable openclaw-node
# Start the service immediately
sudo systemctl start openclaw-node
# Check service status
sudo systemctl status openclaw-node
Verify the Service is Running
# Stream live logs
sudo journalctl -u openclaw-node -f
# View log file
sudo tail -f /var/log/openclaw/node.log
# Typical successful startup log:
# [2026-04-26 09:00:00] OpenClaw Node v4.2.1 starting...
# [2026-04-26 09:00:01] Connecting to Gateway: 192.168.1.100:18789
# [2026-04-26 09:00:02] WebSocket connected
# [2026-04-26 09:00:02] Capabilities declared: system.run, system.which
# [2026-04-26 09:00:03] Node status: online (node-a1b2c3d4)
37.8 Camera Integration and Automated Reporting
Hardware Preparation
USB Camera (Logitech C920 recommended):
# Check camera detection
lsusb | grep -i logitech
# Bus 002 Device 003: ID 046d:082d Logitech, Inc. HD Pro Webcam C920
ls /dev/video*
# /dev/video0 /dev/video1
# Install ffmpeg
sudo apt install -y ffmpeg v4l-utils
# Test a photo capture
ffmpeg -f v4l2 -i /dev/video0 -frames:v 1 /tmp/test.jpg
Raspberry Pi Camera Module 3 (CSI interface):
# Enable CSI camera on Ubuntu
echo "dtoverlay=imx708" | sudo tee -a /boot/firmware/config.txt
# On Pi OS, use libcamera
sudo apt install -y libcamera-apps
libcamera-jpeg -o /tmp/test.jpg --width 1920 --height 1080
Adding camera.snap Capability to the Node
Edit /etc/openclaw/node.json:
{
"nodeId": "node-a1b2c3d4",
"displayName": "Pi Camera Node",
"gateway": {
"host": "192.168.1.100",
"port": 18789
},
"capabilities": ["system.run", "system.which", "camera.snap"],
"camera": {
"device": "/dev/video0",
"snapCommand": "ffmpeg -f v4l2 -i /dev/video0 -frames:v 1 {output} -y",
"outputDir": "/tmp/openclaw-camera"
}
}
Scheduled Automatic Photo Reporting
Create a Cron task (via Control UI):
# Take a photo every 30 minutes
*/30 * * * * node_invoke camera.snap --node node-a1b2c3d4 --upload-to memory
Or trigger a custom script via system.run:
# /usr/local/bin/camera-snap.sh
#!/bin/bash
OUTPUT="/tmp/openclaw-camera/snap-$(date +%Y%m%d-%H%M%S).jpg"
mkdir -p /tmp/openclaw-camera
ffmpeg -f v4l2 -i /dev/video0 -frames:v 1 "$OUTPUT" -y -loglevel quiet
echo "$OUTPUT"
37.9 Sensor Data Collection (GPIO/I2C)
DHT22 Temperature and Humidity Sensor
# Install Python GPIO library
sudo apt install -y python3-pip
pip3 install adafruit-circuitpython-dht
# Read script: /usr/local/bin/read-dht22.py
#!/usr/bin/env python3
import adafruit_dht
import board
import json
import sys
dht = adafruit_dht.DHT22(board.D4) # GPIO4
try:
temperature = dht.temperature
humidity = dht.humidity
result = {
"temperature_c": round(temperature, 1),
"temperature_f": round(temperature * 9/5 + 32, 1),
"humidity_percent": round(humidity, 1),
"timestamp": __import__('time').time()
}
print(json.dumps(result))
except Exception as e:
print(json.dumps({"error": str(e)}), file=sys.stderr)
sys.exit(1)
The Agent calls this via system.run:
python3 /usr/local/bin/read-dht22.py
# {"temperature_c": 24.5, "temperature_f": 76.1, "humidity_percent": 58.3, "timestamp": 1745625600}
BMP280 Barometric Pressure Sensor (I2C)
# Enable I2C
sudo raspi-config nonint do_i2c 0
# Or edit /boot/firmware/config.txt and add: dtparam=i2c_arm=on
# Detect I2C devices
i2cdetect -y 1
# 77: BMP280 default address
pip3 install adafruit-circuitpython-bmp280
#!/usr/bin/env python3
import adafruit_bmp280
import board
import busio
import json
i2c = busio.I2C(board.SCL, board.SDA)
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)
bmp280.sea_level_pressure = 1013.25
print(json.dumps({
"pressure_hpa": round(bmp280.pressure, 2),
"altitude_m": round(bmp280.altitude, 1),
"temperature_c": round(bmp280.temperature, 1)
}))
37.10 Power Consumption Optimization
CPU Frequency Scaling
# Check current frequency
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
# Set conservative governor (good for sustained light loads)
echo "conservative" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# Permanent setting (edit /boot/firmware/config.txt)
# arm_freq=1500 # Pi 4B default is 1800; lowering to 1500 saves ~15% power
# over_voltage=0
Disable Unused Hardware
# Disable Wi-Fi (when using wired ethernet)
sudo rfkill block wifi
# Disable Bluetooth
sudo rfkill block bluetooth
# Turn off HDMI output (saves ~25mA when no monitor is attached)
# Add to /etc/rc.local:
# /usr/bin/tvservice -o
# Disable USB power (Pi 4B, when no USB devices are needed)
# echo '1-1' | sudo tee /sys/bus/usb/drivers/usb/unbind
Typical Power Consumption Reference (Pi 4B)
| Configuration | Typical Power |
|---|---|
| Full load (1.8GHz + USB devices) | ~6.4W |
| Idle (Wi-Fi disabled, no USB) | ~2.7W |
| Optimized (underclocked + HDMI off) | ~2.1W |
| Deep sleep (not suitable for Always-On) | ~1.4W |
37.11 Common Troubleshooting
Issue 1: Node Frequently Disconnects and Reconnects
# Check network stability
ping -c 100 192.168.1.100 | tail -5
# If packet loss > 1%, investigate network hardware first
# Check if Gateway is overloaded
openclaw gateway status
# Increase reconnect backoff time (prevent storm)
# Edit node.json:
# "reconnect": { "initialDelay": 10, "maxDelay": 600, "multiplier": 3 }
Issue 2: system.run Execution Fails
# Check executable paths
which python3 ffmpeg bash
# Test permissions as the openclaw-node user
sudo -u openclaw-node /usr/bin/python3 -c "print('ok')"
# If /dev/video0 access is needed, add user to the video group
sudo usermod -aG video openclaw-node
sudo systemctl restart openclaw-node
Issue 3: Out of Memory (OOM)
# Check memory usage
free -h
sudo journalctl -k | grep -i "oom\|killed"
# Node process already has MemoryMax=512M in the systemd unit file
# If system-wide memory is insufficient, add swap
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Issue 4: Node Does Not Appear in Gateway
# Confirm firewall allows outbound port 18789
sudo ufw status
# Test TCP connectivity
nc -zv 192.168.1.100 18789
# Check Node logs
sudo journalctl -u openclaw-node --since "5 minutes ago"
# Common causes: wrong Gateway address, TLS configuration mismatch
Issue 5: Camera Permission Denied
# Check /dev/video0 permissions
ls -la /dev/video0
# crw-rw----+ 1 root video 81, 0 Apr 26 09:00 /dev/video0
# Confirm user is in the video group (service restart required to take effect)
sudo usermod -aG video openclaw-node
sudo systemctl restart openclaw-node
groups openclaw-node
# openclaw-node : openclaw-node video
37.12 Updates and Maintenance
Updating OpenClaw Node
# Check current version
openclaw --version
# Update to the latest stable version
sudo npm update -g @openclaw/cli
# Restart the service after update
sudo systemctl restart openclaw-node
Log Rotation Configuration
Create /etc/logrotate.d/openclaw-node:
/var/log/openclaw/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
postrotate
systemctl kill --signal=USR1 openclaw-node.service 2>/dev/null || true
endscript
}
37.13 Summary
With the configuration covered in this chapter, your Raspberry Pi is now:
- An always-online Agent Node with automatic reconnection on disconnect
- Securely running under a dedicated system user with minimal privileges
- Observable with complete, rotation-managed logs
- An extensible platform — cameras, sensors, and GPIO control can be added at any time
Combined with the capability matrix introduced in Chapter 36, you now have complete command of the full technical path for integrating physical devices into OpenClaw Agent. The next chapter focuses on deep usage of the Control UI.
Next Chapter: Chapter 38 — Control UI Deep Dive: Configuration Editing, Live Logs, Approval Management, and Dream Diary