AI Agent Cluster Feasibility Study: OpenClaw on PicoCluster¶
Document Version: 1.0
Date: 2026-01-30
Status: Research
Author: AI Assistant
Table of Contents¶
- Executive Summary
- Hardware Analysis: PicoCluster RockPro64
- OpenClaw: Framework Deep Dive
- Cluster Orchestration: k3s vs Docker Swarm
- LLM Backend: Claude Opus 4.5 API
- Use Case Architectures
- MVP: Single Moltbot on k3s ← Start Here
- Backup & Restore Strategy
- Multi-Agent Deployment: Ross, Rachel, Chandler & Joey
- Full Cluster Deployment Strategy
- SD Card Images and Setup
- Cost Analysis
- Risks and Mitigations
- Recommendations
- Appendices
Executive Summary¶
This document evaluates the feasibility of running AI agent frameworks (OpenClaw/ClawdBot) on a 5-node PicoCluster built with RockPro64 single-board computers. The goal is to create a self-hosted, privacy-focused AI agent infrastructure capable of handling:
- Software development automation
- 3D print farm operations
- Marketing and social media management
- Customer support automation
Key Findings¶
| Aspect | Assessment | Notes |
|---|---|---|
| Hardware Capability | Sufficient | RockPro64 provides adequate CPU/RAM for agent workloads |
| Orchestration | k3s Recommended | Lower overhead than Docker Swarm with ARM64 native support |
| Agent Framework | OpenClaw Recommended | More mature, better Docker support, extensive integrations |
| LLM Backend | Claude Opus 4.5 API | No local inference needed; all LLM work via Anthropic API |
| Feasibility | High | Architecture is viable with proper configuration |
Clarification: OpenClaw vs ClawdBot vs ClawdBody¶
The ecosystem has multiple related but distinct components:
OpenClaw / ClawdBot / Moltbot (Core Framework)¶
- OpenClaw is the official project name (also known as "ClawdBot" or "Moltbot")
- The project is hosted at
github.com/openclaw/openclaw - Documentation is available at
docs.clawd.bot - The mascot is a lobster (hence "Claw" references)
- This is the core AI agent framework that runs on individual machines
ClawdBody (VM Orchestration Layer)¶
- ClawdBody (https://clawdbody.com) is a separate product
- Provides preconfigured VM images for deploying OpenClaw instances
- Designed as a management plane for provisioning and managing multiple ClawdBot instances
- Simplifies fleet deployment across cloud providers (Hetzner, AWS, GCP, etc.)
- Status: Website shows "Initializing..." - may still be in development/beta
Architecture Relationship¶
┌─────────────────────────────────────────────────────────┐
│ ClawdBody │
│ (VM Management / Orchestration Layer) │
│ - Preconfigured VM images │
│ - Easy instance provisioning │
│ - Fleet management │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ OpenClaw / Moltbot │
│ (Core Agent Framework) │
│ - Gateway daemon │
│ - Multi-agent routing (built-in) │
│ - Multiple gateways support (via --profile) │
│ - Channel integrations │
└─────────────────────────────────────────────────────────┘
For PicoCluster Deployment¶
For your use case, you have two options:
- Direct OpenClaw deployment - Deploy OpenClaw directly on k3s (recommended for ARM64 cluster)
- Wait for ClawdBody - If ClawdBody adds ARM64 VM image support, it could simplify management
Since ClawdBody appears to target x86 VMs on cloud providers, and your PicoCluster uses ARM64, direct OpenClaw deployment via k3s/Docker is the recommended approach.
There is also an unrelated project called "OpenClaw" which is a Captain Claw game reimplementation—this is not the AI agent framework.
Hardware Analysis: PicoCluster RockPro64¶
Ordered Configuration¶
Based on the provided shopping cart:
| Component | Specification | Price |
|---|---|---|
| Pico 5 RockPro64 | Model 5H, Assembled Cube | $839.00 |
| Application Cluster Sets | 640GB (5× 128GB SD cards), Kubernetes pre-configured | $340.00 |
| Accessories | Included | — |
| Total | $1,179.00 |
RockPro64 Specifications (per node)¶
| Component | Specification |
|---|---|
| SoC | Rockchip RK3399 |
| CPU | Hexa-core: 2× ARM Cortex-A72 @ 2.0GHz + 4× ARM Cortex-A53 @ 1.5GHz |
| RAM | 4GB LPDDR4 (2GB also available) |
| GPU | Mali-T860 MP4 (not utilized for LLM work) |
| Storage | microSD + optional eMMC (up to 128GB) + PCIe x4 slot |
| Network | Gigabit Ethernet |
| USB | 2× USB 3.0, 1× USB-C (with DP alt mode) |
| Power | 12V/3A DC input |
Cluster Total Resources¶
| Resource | Total (5 nodes) | Usable (after k3s overhead) |
|---|---|---|
| CPU Cores | 30 cores (10 high-perf, 20 efficiency) | ~28 cores |
| RAM | 20GB (assuming 4GB/node) | ~17GB |
| Storage | 640GB SD | ~600GB |
| Network | 5Gbps aggregate | Full speed internal |
Hardware Suitability Assessment¶
Strengths:
- ARM Cortex-A72 cores provide good single-threaded performance for Node.js workloads
- 4GB RAM per node is adequate for agent gateway processes
- PCIe slot available for NVMe upgrade if SD performance is insufficient
- Gigabit networking enables efficient inter-node communication
Limitations:
- No local LLM inference capability (GPU not suitable, RAM insufficient)
- SD card I/O may bottleneck during heavy logging/database operations
- Limited RAM for browser automation sandboxes (1GB per sandbox recommended)
Verdict: Hardware is well-suited for API-based AI agents that delegate LLM inference to cloud services.
OpenClaw: Framework Deep Dive¶
Project Overview¶
OpenClaw is an open-source, self-hosted personal AI assistant framework that enables autonomous task execution across multiple platforms.
| Attribute | Value |
|---|---|
| GitHub Stars | 107,000+ |
| License | MIT |
| Language | TypeScript/Node.js |
| Container Support | Docker, Docker Compose |
| ARM64 Support | Yes (Node.js runs natively) |
Core Architecture¶
┌─────────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway │
│ (Single long-lived daemon) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Channels │ │ Tools │ │ Skills │ │
│ │ - WhatsApp │ │ - exec │ │ - Custom │ │
│ │ - Telegram │ │ - browser │ │ - ClawdHub │ │
│ │ - Discord │ │ - read/write│ │ - Community │ │
│ │ - Slack │ │ - web fetch │ │ │ │
│ │ - Signal │ │ - apply_patch│ │ │ │
│ │ - iMessage │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Agents │ │ Sessions │ │ Memory │ │
│ │ - Multi-agent│ │ - Persistent│ │ - Context │ │
│ │ - Subagents │ │ - Pruning │ │ - Per-user │ │
│ │ - Routing │ │ - Compaction│ │ - Semantic │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────┤
│ LLM Providers │
│ Anthropic Claude │ OpenAI GPT │ Local (Ollama) │ OpenRouter │
└─────────────────────────────────────────────────────────────────┘
Key Features¶
Multi-Agent Routing¶
OpenClaw supports running multiple specialized agents with routing based on:
- User identity
- Channel (WhatsApp vs Discord vs Slack)
- Message content/intent
- Time-based rules
{
"agents": {
"defaults": {
"model": "claude-opus-4.5"
},
"list": [
{
"id": "dev-agent",
"name": "Developer Assistant",
"channels": ["discord"],
"skills": ["coding", "git", "docker"]
},
{
"id": "print-agent",
"name": "Print Farm Manager",
"channels": ["telegram"],
"skills": ["octoprint", "klipper", "scheduling"]
},
{
"id": "support-agent",
"name": "Customer Support",
"channels": ["whatsapp", "webchat"],
"skills": ["faq", "ticketing", "escalation"]
}
]
}
}
Sandboxed Execution¶
Agent tool execution can be isolated in Docker containers:
{
"agents": {
"defaults": {
"sandbox": {
"mode": "non-main",
"scope": "agent",
"docker": {
"image": "openclaw-sandbox:bookworm-slim",
"memory": "1g",
"cpus": 1,
"network": "none"
}
}
}
}
}
Automation Capabilities¶
- Cron Jobs: Scheduled task execution
- Webhooks: Event-driven triggers
- Heartbeats: Proactive check-ins
- Polling: Regular data source checks
Memory & Persistence Architecture¶
OpenClaw uses a file-based storage model - no PostgreSQL or Redis required:
| Storage Type | Location | Format | Purpose |
|---|---|---|---|
| Daily memory logs | workspace/memory/YYYY-MM-DD.md |
Markdown | Append-only daily notes |
| Long-term memory | workspace/MEMORY.md |
Markdown | Curated persistent facts |
| Session transcripts | agents/<agentId>/sessions/*.jsonl |
JSONL | Conversation history |
| Vector embeddings | memory/<agentId>.sqlite |
SQLite | Semantic search index |
| Config/credentials | ~/.openclaw/ |
JSON | Gateway and channel config |
Key characteristics: - Plain Markdown is the source of truth - The model only "remembers" what's written to disk - SQLite for vector search - Embeddings stored in per-agent SQLite (no separate database server) - No external database dependencies - Perfect for resource-constrained ARM64 nodes - Hybrid search - Combines BM25 (keyword) + vector similarity for best recall - Automatic memory flush - Before context compaction, memories are written to disk
Directory structure per agent:
~/.openclaw/
├── config.json # Gateway configuration
├── workspace/ # Agent workspace
│ ├── MEMORY.md # Long-term curated memory
│ └── memory/
│ ├── 2026-01-30.md # Daily memory log
│ └── 2026-01-29.md
├── agents/
│ └── <agentId>/
│ └── sessions/
│ └── *.jsonl # Session transcripts
└── memory/
└── <agentId>.sqlite # Vector embeddings index
For Kubernetes deployment - PVC is sufficient:
# Each Moltbot pod needs a PVC mounted at /home/node/.openclaw
volumes:
- name: data
persistentVolumeClaim:
claimName: moltbot-data # Contains ALL memory, sessions, config
Memory search configuration (optional, for semantic search):
{
"agents": {
"defaults": {
"memorySearch": {
"provider": "gemini",
"model": "gemini-embedding-001",
"query": {
"hybrid": {
"enabled": true,
"vectorWeight": 0.7,
"textWeight": 0.3
}
}
}
}
}
}
Resource Requirements¶
| Component | RAM | CPU | Disk |
|---|---|---|---|
| OpenClaw Gateway | 512MB–1GB | 0.5 core | 500MB |
| Per-Agent Sandbox | 1GB | 1 core | 100MB |
| Browser Sandbox | 2GB | 1 core | 500MB |
| SQLite memory index | ~50MB | minimal | 100MB–1GB |
Docker Deployment¶
OpenClaw provides official Docker support:
# Quick setup
./docker-setup.sh
# Or manual
docker build -t openclaw:local -f Dockerfile .
docker compose up -d openclaw-gateway
Docker Compose configuration:
services:
openclaw-gateway:
image: openclaw:local
ports:
- '18789:18789' # Gateway API
- '18793:18793' # Canvas host
volumes:
- ~/.openclaw:/home/node/.openclaw
- ./workspace:/home/node/workspace
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OPENCLAW_GATEWAY_TOKEN=${GATEWAY_TOKEN}
restart: unless-stopped
Multiple Gateways / Fleet Support¶
OpenClaw natively supports running multiple isolated gateways on the same host using profiles:
# Main gateway
moltbot --profile main setup
moltbot --profile main gateway --port 18789
# Rescue/secondary gateway
moltbot --profile rescue setup
moltbot --profile rescue gateway --port 19001
Isolation requirements for multiple gateways:
CLAWDBOT_CONFIG_PATH— per-instance config fileCLAWDBOT_STATE_DIR— per-instance sessions, credentials, cachesagents.defaults.workspace— per-instance workspace rootgateway.port— unique per instance (leave 20+ ports between instances)
This means on your PicoCluster, you can run:
- One OpenClaw gateway per k3s worker node
- Each gateway isolated via profiles
- All managed through k3s orchestration
vs. ClawdBody approach:
ClawdBody would manage VM-level provisioning (spin up entire VMs with OpenClaw pre-installed), while the native --profile approach manages multiple instances on existing infrastructure. For a bare-metal ARM64 cluster, the native approach is more appropriate.
Claude/Anthropic Integration¶
OpenClaw has first-class Anthropic support:
{
"model": {
"provider": "anthropic",
"model": "claude-opus-4.5",
"apiKey": "${ANTHROPIC_API_KEY}"
}
}
Supported Anthropic models:
- Claude Opus 4.5 (recommended for complex reasoning)
- Claude Sonnet 4.5 (balanced performance/cost)
- Claude Haiku (fast, inexpensive)
Cluster Orchestration: k3s vs Docker Swarm¶
Comparison Matrix¶
| Feature | k3s | Docker Swarm |
|---|---|---|
| ARM64 Support | Native, certified | Native |
| Memory Overhead | ~512MB control plane | ~100MB built-in |
| Learning Curve | Moderate (Kubernetes) | Low (Docker native) |
| Service Discovery | CoreDNS, full K8s | Built-in overlay |
| Load Balancing | Traefik (bundled) | Built-in |
| Storage | Local-path, Longhorn | Docker volumes |
| Monitoring | Prometheus/Grafana | Limited built-in |
| Self-Healing | Full pod lifecycle | Container restart |
| Community | Very active | Declining |
| Edge Certified | Yes (CNCF) | No |
Recommendation: k3s¶
k3s is the recommended choice for this deployment:
- ARM64 Optimization: k3s is specifically designed for ARM and edge devices
- Single Binary: <70MB, minimal attack surface
- SQLite Backend: No etcd cluster needed for 5 nodes
- Traefik Included: Built-in ingress controller
- Future-Proof: Growing ecosystem vs Swarm's stagnation
k3s Architecture for 5-Node Cluster¶
┌─────────────────────────────────────────────────────────────────┐
│ k3s Cluster Topology │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Node 1 │ ← k3s Server (control plane) │
│ │ RockPro64 │ - API Server │
│ │ 4GB RAM │ - Scheduler │
│ │ │ - SQLite datastore │
│ └─────────────┘ │
│ │ │
│ ├───────────────┬───────────────┬───────────────┐ │
│ │ │ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼────┐│
│ │ Node 2 │ │ Node 3 │ │ Node 4 │ │ Node 5 ││
│ │ Worker │ │ Worker │ │ Worker │ │ Worker ││
│ │ RockPro64 │ │ RockPro64 │ │ RockPro64 │ │ RockPro64 ││
│ │ 4GB RAM │ │ 4GB RAM │ │ 4GB RAM │ │ 4GB RAM ││
│ └─────────────┘ └─────────────┘ └─────────────┘ └───────────┘│
│ │
└─────────────────────────────────────────────────────────────────┘
Resource Allocation Strategy¶
| Node | Role | Workloads |
|---|---|---|
| Node 1 | Server + Light Agent | k3s control plane, monitoring agent |
| Node 2 | Worker | Development Agent + Sandbox |
| Node 3 | Worker | Print Farm Agent + OctoPrint proxy |
| Node 4 | Worker | Marketing/Social Agent |
| Node 5 | Worker | Customer Support Agent + Database |
LLM Backend: Claude Opus 4.5 API¶
Pricing Structure¶
| Metric | Cost |
|---|---|
| Input Tokens | $5.00 / million tokens |
| Output Tokens | $25.00 / million tokens |
| Cached Input | $0.50 / million tokens (90% savings) |
| Context Window | 200,000 tokens |
Cost Optimization Strategies¶
- Prompt Caching: Reuse system prompts and common context
- Batch Processing: 50% discount for non-real-time workloads
- Model Tiering: Use Haiku for simple tasks, Opus for complex reasoning
- Context Compaction: OpenClaw's built-in session compaction reduces tokens
Monthly Cost Estimates¶
| Use Case | Est. Token Usage | Monthly Cost |
|---|---|---|
| Development Agent | 10M input / 2M output | $100 |
| Print Farm Agent | 5M input / 1M output | $50 |
| Marketing Agent | 8M input / 3M output | $115 |
| Support Agent | 15M input / 5M output | $200 |
| Total | ~$465/month |
Estimates assume moderate usage. Actual costs depend on conversation length and frequency.
API Configuration¶
# Environment variable
export ANTHROPIC_API_KEY="sk-ant-..."
# OpenClaw configuration
{
"providers": {
"anthropic": {
"apiKey": "${ANTHROPIC_API_KEY}",
"defaultModel": "claude-opus-4.5"
}
}
}
Use Case Architectures¶
1. Development Agent¶
Purpose: Autonomous code development, testing, and PR management
Capabilities:
- Code generation and review
- Git operations (commit, branch, PR)
- Test execution and debugging
- CI/CD integration via webhooks
Architecture:
# Kubernetes deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: dev-agent
spec:
replicas: 1
template:
spec:
containers:
- name: openclaw
image: openclaw:arm64
env:
- name: AGENT_ID
value: 'dev-agent'
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: anthropic
key: api-key
volumeMounts:
- name: workspace
mountPath: /workspace
- name: ssh-keys
mountPath: /home/node/.ssh
readOnly: true
volumes:
- name: workspace
persistentVolumeClaim:
claimName: dev-workspace
- name: ssh-keys
secret:
secretName: github-ssh
Integrations:
- GitHub/GitLab via SSH
- Discord channel for commands
- Sentry webhooks for error capture
2. 3D Print Farm Operations Agent¶
Purpose: Automated print farm management, monitoring, and scheduling
Capabilities:
- Print job scheduling and queue management
- Failure detection and alerting
- Filament inventory tracking
- Remote printer control
Architecture:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Telegram │────▶│ OpenClaw │────▶│ Moonraker │
│ Channel │ │ Print Agent │ │ API │
└──────────────┘ └──────────────┘ └──────────────┘
│ │
│ ┌─────▼─────┐
│ │ Klipper │
│ │ Firmware │
│ └───────────┘
▼
┌──────────────┐
│ OctoEverywhere │ (AI failure detection)
└──────────────┘
Custom Skills:
// skills/print-farm.js
export const printFarmSkill = {
name: 'print-farm',
description: 'Manage 3D print farm operations',
tools: [
{
name: 'start_print',
description: 'Start a print job on specified printer',
parameters: {
printer_id: { type: 'string' },
gcode_file: { type: 'string' },
},
handler: async ({ printer_id, gcode_file }) => {
const moonraker = new MoonrakerAPI(printer_id);
return await moonraker.startPrint(gcode_file);
},
},
{
name: 'check_status',
description: 'Get current status of all printers',
handler: async () => {
const printers = await getAllPrinters();
return printers.map((p) => ({
id: p.id,
status: p.state,
progress: p.progress,
eta: p.eta,
}));
},
},
],
};
Moonraker Integration:
# Moonraker provides JSON-RPC and REST APIs
# WebSocket: ws://printer-ip:7125/websocket
# HTTP: http://printer-ip:7125/api
endpoints = {
"start_print": "POST /printer/print/start",
"cancel_print": "POST /printer/print/cancel",
"get_status": "GET /printer/objects/query",
"gcode_upload": "POST /server/files/upload"
}
3. Marketing and Social Media Agent¶
Purpose: Content creation, scheduling, and social media management
Capabilities:
- Content generation and editing
- Post scheduling across platforms
- Analytics monitoring
- Trend analysis
Architecture:
apiVersion: apps/v1
kind: Deployment
metadata:
name: marketing-agent
spec:
template:
spec:
containers:
- name: openclaw
image: openclaw:arm64
env:
- name: AGENT_ID
value: 'marketing-agent'
# Social media API keys
- name: TWITTER_API_KEY
valueFrom:
secretKeyRef:
name: social-apis
key: twitter
- name: INSTAGRAM_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: social-apis
key: instagram
Cron Jobs:
{
"cron": [
{
"schedule": "0 9 * * *",
"action": "morning_content_review",
"message": "Review scheduled posts for today and suggest improvements"
},
{
"schedule": "0 18 * * 5",
"action": "weekly_analytics",
"message": "Generate weekly social media performance report"
}
]
}
4. Customer Support Agent¶
Purpose: Automated customer inquiry handling and escalation
Capabilities:
- FAQ responses
- Order status lookups
- Ticket creation and routing
- Sentiment analysis for escalation
Architecture:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ WhatsApp │────▶│ OpenClaw │────▶│ Shopify │
│ Business │ │Support Agent │ │ API │
└──────────────┘ └──────────────┘ └──────────────┘
│
├───────────────────────────┐
│ │
┌──────▼──────┐ ┌────────▼───────┐
│ Zendesk │ │ Escalation │
│ Tickets │ │ (Human Team) │
└─────────────┘ └────────────────┘
Webhook Configuration:
{
"webhooks": [
{
"path": "/shopify/orders",
"action": "process_order_update",
"verify": "shopify_hmac"
},
{
"path": "/zendesk/tickets",
"action": "process_ticket_update"
}
]
}
MVP: Single Moltbot on k3s (Quick Start)¶
This section provides the minimum viable setup to get one Moltbot running on k3s on a single RockPro64 node. This establishes the foundation for scaling to the full cluster.
Prerequisites¶
- 1× RockPro64 with 128GB SD card
- Anthropic API key from https://console.anthropic.com/
- Telegram bot token from @BotFather (easiest channel to start)
- A workstation to flash the SD card
Step 1: Flash Armbian and Boot¶
# On your workstation - download Armbian Minimal (recommended for k3s)
# Ubuntu 24.04 Noble Minimal - smallest footprint, optimized for containers
wget https://dl.armbian.com/rockpro64/Noble_current_minimal
# Alternative: Debian 12 Bookworm Minimal
# wget https://dl.armbian.com/rockpro64/Bookworm_current_minimal
# Verify checksum
wget https://dl.armbian.com/rockpro64/Noble_current_minimal.sha
sha256sum -c Noble_current_minimal.sha
# Flash to SD card (repeat for each of the 5 nodes)
xz -d Armbian_*_Rockpro64_noble_current_*.img.xz
sudo dd if=Armbian_*_Rockpro64_noble_current_*.img of=/dev/sdX bs=4M status=progress
sync
Insert SD card, power on, and SSH in (default: root / 1234).
Step 2: System Preparation (on the RockPro64)¶
# Set hostname
hostnamectl set-hostname pico-node1
# Update system
apt update && apt upgrade -y
# Install prerequisites
apt install -y curl open-iscsi nfs-common
# Add swap (important for 4GB RAM nodes)
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# Disable kernel swap limit (k3s requirement)
# Note: k3s works with swap enabled as of v1.28+
Step 3: Install k3s (Single-Node Mode)¶
# Install k3s as both server and agent (single node)
curl -sfL https://get.k3s.io | sh -s - server \
--write-kubeconfig-mode 644 \
--disable traefik \
--flannel-backend host-gw
# Wait for k3s to be ready
kubectl wait --for=condition=Ready node/pico-node1 --timeout=120s
# Verify k3s is running
kubectl get nodes
kubectl get pods -A
Step 4: Create Namespace and Secrets¶
# Create namespace for Moltbot
kubectl create namespace moltbot
# Create secret for Anthropic API key
kubectl create secret generic anthropic-credentials \
--namespace moltbot \
--from-literal=api-key="sk-ant-YOUR_API_KEY_HERE"
# Create secret for Telegram bot token
kubectl create secret generic telegram-credentials \
--namespace moltbot \
--from-literal=bot-token="YOUR_TELEGRAM_BOT_TOKEN"
# Generate gateway token
export GATEWAY_TOKEN=$(openssl rand -hex 32)
kubectl create secret generic gateway-credentials \
--namespace moltbot \
--from-literal=token="$GATEWAY_TOKEN"
Step 5: Create Persistent Storage¶
# Create PersistentVolumeClaim for Moltbot data
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: moltbot-data
namespace: moltbot
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 10Gi
EOF
Step 6: Deploy Moltbot¶
# Create the Moltbot deployment
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: moltbot
namespace: moltbot
labels:
app: moltbot
spec:
replicas: 1
selector:
matchLabels:
app: moltbot
template:
metadata:
labels:
app: moltbot
spec:
containers:
- name: moltbot
image: clawdbot/clawdbot:latest
ports:
- containerPort: 18789
name: gateway
- containerPort: 18793
name: canvas
env:
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: anthropic-credentials
key: api-key
- name: CLAWDBOT_GATEWAY_TOKEN
valueFrom:
secretKeyRef:
name: gateway-credentials
key: token
- name: CLAWDBOT_GATEWAY_BIND
value: "0.0.0.0"
volumeMounts:
- name: data
mountPath: /home/node/.openclaw
- name: workspace
mountPath: /home/node/workspace
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: moltbot-data
- name: workspace
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: moltbot
namespace: moltbot
spec:
type: NodePort
selector:
app: moltbot
ports:
- name: gateway
port: 18789
targetPort: 18789
nodePort: 30789
- name: canvas
port: 18793
targetPort: 18793
nodePort: 30793
EOF
Step 7: Run Onboarding¶
# Wait for pod to be ready
kubectl wait --for=condition=Ready pod -l app=moltbot -n moltbot --timeout=120s
# Get pod name
POD_NAME=$(kubectl get pods -n moltbot -l app=moltbot -o jsonpath='{.items[0].metadata.name}')
# Run interactive onboarding
kubectl exec -it $POD_NAME -n moltbot -- moltbot onboard
# Or configure Telegram directly
kubectl exec -it $POD_NAME -n moltbot -- moltbot channels add --channel telegram
During onboarding:
- Name your agent (e.g., "PrintFarmBot")
- AI provider → Already configured via env var (Anthropic)
- Model → Select "claude-opus-4.5"
- Channels → Configure Telegram with your bot token
Step 8: Verify Deployment¶
# Check pod status
kubectl get pods -n moltbot
# Check logs
kubectl logs -f deployment/moltbot -n moltbot
# Check health inside container
kubectl exec $POD_NAME -n moltbot -- moltbot health
# Check channel status
kubectl exec $POD_NAME -n moltbot -- moltbot channels status
Step 9: Test Your Agent¶
- Open Telegram
- Find your bot by username
- Send: "Hello, what can you do?"
- Your Moltbot should respond!
MVP Verification Checklist¶
# Run all checks
kubectl get nodes # Node Ready
kubectl get pods -n moltbot # Pod Running
kubectl exec $POD_NAME -n moltbot -- moltbot status # Gateway running
kubectl exec $POD_NAME -n moltbot -- moltbot health # Health OK
- k3s node is Ready
- Moltbot pod is Running
- Gateway health check passes
- Telegram channel connected
- Agent responds to messages
MVP Resource Usage (Expected)¶
| Component | RAM | CPU |
|---|---|---|
| k3s control plane | ~500MB | 5% |
| Moltbot gateway | 400-800MB | 5-15% |
| Total | ~1.3GB | ~20% |
| Available | ~2.7GB | ~80% |
Useful Commands Reference¶
# View logs
kubectl logs -f deployment/moltbot -n moltbot
# Shell into container
kubectl exec -it $POD_NAME -n moltbot -- /bin/bash
# Restart deployment
kubectl rollout restart deployment/moltbot -n moltbot
# Scale (for testing)
kubectl scale deployment/moltbot -n moltbot --replicas=0
kubectl scale deployment/moltbot -n moltbot --replicas=1
# Delete everything and start fresh
kubectl delete namespace moltbot
Next Steps After MVP¶
Once the single-node k3s MVP is working:
- Add worker nodes - Join nodes 2-5 to the cluster
- Add more channels - Discord, WhatsApp, Slack
- Create specialized agents - Dev, Print Farm, Support, Marketing
- Deploy monitoring - Prometheus + Grafana
- Set up backups - See backup section below
Backup & Restore Strategy¶
Since all Moltbot state lives in the PVC, backup is straightforward. This section covers multiple approaches from simple manual backups to automated solutions.
What Needs to Be Backed Up¶
| Data | Location in PVC | Priority | Size Estimate |
|---|---|---|---|
| Agent memory | workspace/MEMORY.md, workspace/memory/*.md |
Critical | 1-100MB |
| Session history | agents/<id>/sessions/*.jsonl |
High | 10-500MB |
| Configuration | config.json, credentials |
Critical | <1MB |
| Vector index | memory/<id>.sqlite |
Low (regenerable) | 100MB-1GB |
| Skills/plugins | workspace/skills/ |
Medium | 1-50MB |
Total typical backup size: 50MB - 1GB per agent
Method 1: Manual Backup (Quick & Simple)¶
# Get pod name
POD_NAME=$(kubectl get pods -n moltbot -l app=moltbot -o jsonpath='{.items[0].metadata.name}')
# Create backup directory with timestamp
BACKUP_DIR="moltbot-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p $BACKUP_DIR
# Copy data from pod to local machine
kubectl cp moltbot/$POD_NAME:/home/node/.openclaw $BACKUP_DIR/openclaw
# Compress
tar -czvf $BACKUP_DIR.tar.gz $BACKUP_DIR
# Upload to external storage (optional)
# rclone copy $BACKUP_DIR.tar.gz remote:backups/moltbot/
Method 2: Scheduled Backup CronJob¶
Deploy a CronJob that backs up PVC data to a secondary location:
apiVersion: batch/v1
kind: CronJob
metadata:
name: moltbot-backup
namespace: moltbot
spec:
schedule: "0 2 * * *" # Daily at 2 AM
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: alpine:latest
command:
- /bin/sh
- -c
- |
set -e
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="/backup/moltbot-$TIMESTAMP.tar.gz"
# Create compressed backup
cd /data
tar -czvf $BACKUP_FILE \
--exclude='*.sqlite' \
.
# Keep only last 7 backups
ls -t /backup/moltbot-*.tar.gz | tail -n +8 | xargs -r rm
echo "Backup completed: $BACKUP_FILE"
ls -lh /backup/
volumeMounts:
- name: data
mountPath: /data
readOnly: true
- name: backup
mountPath: /backup
restartPolicy: OnFailure
volumes:
- name: data
persistentVolumeClaim:
claimName: moltbot-data
- name: backup
persistentVolumeClaim:
claimName: moltbot-backup # Separate PVC for backups
---
# PVC for storing backups
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: moltbot-backup
namespace: moltbot
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 20Gi
Method 3: Backup to External Storage (S3/NFS)¶
For off-cluster backups, use rclone or restic:
apiVersion: batch/v1
kind: CronJob
metadata:
name: moltbot-backup-s3
namespace: moltbot
spec:
schedule: "0 3 * * *" # Daily at 3 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: rclone/rclone:latest
command:
- /bin/sh
- -c
- |
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
# Sync to S3-compatible storage (Backblaze B2, MinIO, etc.)
rclone sync /data remote:moltbot-backups/$TIMESTAMP \
--exclude "*.sqlite" \
--transfers 4 \
--fast-list
# Prune old backups (keep 30 days)
rclone delete remote:moltbot-backups \
--min-age 30d
echo "Backup synced to remote:moltbot-backups/$TIMESTAMP"
env:
- name: RCLONE_CONFIG_REMOTE_TYPE
value: "s3"
- name: RCLONE_CONFIG_REMOTE_PROVIDER
value: "Backblaze" # or Minio, AWS, etc.
- name: RCLONE_CONFIG_REMOTE_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: backup-credentials
key: access-key
- name: RCLONE_CONFIG_REMOTE_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: backup-credentials
key: secret-key
- name: RCLONE_CONFIG_REMOTE_ENDPOINT
value: "s3.us-west-000.backblazeb2.com"
volumeMounts:
- name: data
mountPath: /data
readOnly: true
restartPolicy: OnFailure
volumes:
- name: data
persistentVolumeClaim:
claimName: moltbot-data
Method 4: Velero (Enterprise-Grade)¶
For full cluster backup including PVCs:
# Install Velero CLI
curl -LO https://github.com/vmware-tanzu/velero/releases/download/v1.13.0/velero-v1.13.0-linux-arm64.tar.gz
tar -xvf velero-v1.13.0-linux-arm64.tar.gz
sudo mv velero-v1.13.0-linux-arm64/velero /usr/local/bin/
# Install Velero in cluster (with local-path-provisioner support)
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.9.0 \
--bucket moltbot-backups \
--secret-file ./credentials-velero \
--backup-location-config region=us-east-1,s3ForcePathStyle=true,s3Url=http://minio:9000 \
--use-node-agent \
--default-volumes-to-fs-backup
# Create backup of moltbot namespace
velero backup create moltbot-backup-$(date +%Y%m%d) \
--include-namespaces moltbot \
--default-volumes-to-fs-backup
# Schedule daily backups
velero schedule create moltbot-daily \
--schedule="0 2 * * *" \
--include-namespaces moltbot \
--default-volumes-to-fs-backup \
--ttl 720h # Keep for 30 days
Restore Procedures¶
Restore from Manual Backup¶
# Scale down to avoid conflicts
kubectl scale deployment/moltbot -n moltbot --replicas=0
# Get a temporary pod for restore
kubectl run restore-pod --image=alpine -n moltbot \
--overrides='{"spec":{"containers":[{"name":"restore","image":"alpine","command":["sleep","3600"],"volumeMounts":[{"name":"data","mountPath":"/data"}]}],"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"moltbot-data"}}]}}'
# Wait for pod
kubectl wait --for=condition=Ready pod/restore-pod -n moltbot
# Copy backup into PVC
kubectl cp moltbot-backup-20260130/openclaw moltbot/restore-pod:/data/
# Cleanup and restart
kubectl delete pod restore-pod -n moltbot
kubectl scale deployment/moltbot -n moltbot --replicas=1
Restore from Velero¶
# List available backups
velero backup get
# Restore entire namespace
velero restore create --from-backup moltbot-backup-20260130
# Or restore just the PVC
velero restore create --from-backup moltbot-backup-20260130 \
--include-resources persistentvolumeclaims,persistentvolumes
Backup Verification¶
Always verify backups are valid:
# Test restore to temporary namespace
kubectl create namespace moltbot-test
# Restore backup to test namespace
velero restore create test-restore \
--from-backup moltbot-backup-20260130 \
--namespace-mappings moltbot:moltbot-test
# Verify data integrity
kubectl exec -it deployment/moltbot -n moltbot-test -- moltbot health
kubectl exec -it deployment/moltbot -n moltbot-test -- ls -la /home/node/.openclaw/
# Cleanup test
kubectl delete namespace moltbot-test
Backup Schedule Recommendations¶
| Agent Type | Backup Frequency | Retention |
|---|---|---|
| Production Support | Every 6 hours | 30 days |
| Development | Daily | 14 days |
| Print Farm | Daily | 30 days |
| Marketing | Daily | 14 days |
Multi-Agent Deployment: Ross, Rachel, Chandler & Joey¶
This section provides the complete deployment configuration for 4 named Moltbot agents, each with individual PVCs and backup/restore capability.
Agent Assignment¶
| Agent | Node | Purpose | Channel |
|---|---|---|---|
| Ross | pico-node2 | Development / Code | Discord |
| Rachel | pico-node3 | 3D Print Farm | Telegram |
| Chandler | pico-node4 | Marketing / Social | Slack |
| Joey | pico-node5 | Customer Support |
Node 1 runs the k3s control plane and monitoring.
Cluster Architecture¶
┌─────────────────────────────────────────────────────────────────────────┐
│ PicoCluster k3s │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ pico-node1 │ k3s Server (control plane) │
│ │ (4GB) │ - API Server, Scheduler, CoreDNS │
│ └─────────────┘ - Prometheus + Grafana │
│ │ │
│ ├──────────────┬──────────────┬──────────────┐ │
│ │ │ │ │ │
│ ┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐ │
│ │pico-node2 │ │pico-node3 │ │pico-node4 │ │pico-node5 │ │
│ │ (4GB) │ │ (4GB) │ │ (4GB) │ │ (4GB) │ │
│ │ │ │ │ │ │ │ │ │
│ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │
│ │ │ Ross │ │ │ │Rachel│ │ │ │Chandler│ │ │ │ Joey │ │ │
│ │ │ Dev │ │ │ │Print │ │ │ │Market│ │ │ │Support│ │ │
│ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ ┌──▼───┐ │ │ ┌──▼───┐ │ │ ┌──▼───┐ │ │ ┌──▼───┐ │ │
│ │ │ PVC │ │ │ │ PVC │ │ │ │ PVC │ │ │ │ PVC │ │ │
│ │ │ ross │ │ │ │rachel│ │ │ │chandler│ │ │ │ joey │ │ │
│ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │
│ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Prerequisites¶
# Label nodes for scheduling (run on pico-node1)
kubectl label node pico-node2 agent=ross
kubectl label node pico-node3 agent=rachel
kubectl label node pico-node4 agent=chandler
kubectl label node pico-node5 agent=joey
# Verify labels
kubectl get nodes --show-labels
Complete Deployment Manifest¶
Save this as moltbot-agents.yaml:
# Namespace
apiVersion: v1
kind: Namespace
metadata:
name: moltbot
---
# Shared Secrets
apiVersion: v1
kind: Secret
metadata:
name: anthropic-credentials
namespace: moltbot
type: Opaque
stringData:
api-key: "sk-ant-YOUR_ANTHROPIC_API_KEY"
---
apiVersion: v1
kind: Secret
metadata:
name: gateway-tokens
namespace: moltbot
type: Opaque
stringData:
ross: "GENERATE_RANDOM_TOKEN_1"
rachel: "GENERATE_RANDOM_TOKEN_2"
chandler: "GENERATE_RANDOM_TOKEN_3"
joey: "GENERATE_RANDOM_TOKEN_4"
---
apiVersion: v1
kind: Secret
metadata:
name: channel-credentials
namespace: moltbot
type: Opaque
stringData:
discord-token: "YOUR_DISCORD_BOT_TOKEN"
telegram-token: "YOUR_TELEGRAM_BOT_TOKEN"
slack-token: "YOUR_SLACK_BOT_TOKEN"
whatsapp-token: "YOUR_WHATSAPP_TOKEN"
---
# ============================================
# ROSS - Development Agent (Node 2)
# ============================================
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ross-data
namespace: moltbot
labels:
agent: ross
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ross
namespace: moltbot
labels:
agent: ross
spec:
replicas: 1
selector:
matchLabels:
agent: ross
template:
metadata:
labels:
agent: ross
spec:
nodeSelector:
agent: ross
containers:
- name: moltbot
image: clawdbot/clawdbot:latest
ports:
- containerPort: 18789
name: gateway
- containerPort: 18793
name: canvas
env:
- name: AGENT_NAME
value: "Ross"
- name: AGENT_ROLE
value: "Development & Code Assistant"
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: anthropic-credentials
key: api-key
- name: CLAWDBOT_GATEWAY_TOKEN
valueFrom:
secretKeyRef:
name: gateway-tokens
key: ross
- name: CLAWDBOT_GATEWAY_BIND
value: "0.0.0.0"
- name: DISCORD_TOKEN
valueFrom:
secretKeyRef:
name: channel-credentials
key: discord-token
volumeMounts:
- name: data
mountPath: /home/node/.openclaw
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: ross-data
---
apiVersion: v1
kind: Service
metadata:
name: ross
namespace: moltbot
labels:
agent: ross
spec:
type: NodePort
selector:
agent: ross
ports:
- name: gateway
port: 18789
targetPort: 18789
nodePort: 30001
- name: canvas
port: 18793
targetPort: 18793
nodePort: 30002
---
# ============================================
# RACHEL - 3D Print Farm Agent (Node 3)
# ============================================
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rachel-data
namespace: moltbot
labels:
agent: rachel
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rachel
namespace: moltbot
labels:
agent: rachel
spec:
replicas: 1
selector:
matchLabels:
agent: rachel
template:
metadata:
labels:
agent: rachel
spec:
nodeSelector:
agent: rachel
containers:
- name: moltbot
image: clawdbot/clawdbot:latest
ports:
- containerPort: 18789
name: gateway
- containerPort: 18793
name: canvas
env:
- name: AGENT_NAME
value: "Rachel"
- name: AGENT_ROLE
value: "3D Print Farm Manager"
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: anthropic-credentials
key: api-key
- name: CLAWDBOT_GATEWAY_TOKEN
valueFrom:
secretKeyRef:
name: gateway-tokens
key: rachel
- name: CLAWDBOT_GATEWAY_BIND
value: "0.0.0.0"
- name: TELEGRAM_TOKEN
valueFrom:
secretKeyRef:
name: channel-credentials
key: telegram-token
volumeMounts:
- name: data
mountPath: /home/node/.openclaw
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: rachel-data
---
apiVersion: v1
kind: Service
metadata:
name: rachel
namespace: moltbot
labels:
agent: rachel
spec:
type: NodePort
selector:
agent: rachel
ports:
- name: gateway
port: 18789
targetPort: 18789
nodePort: 30011
- name: canvas
port: 18793
targetPort: 18793
nodePort: 30012
---
# ============================================
# CHANDLER - Marketing Agent (Node 4)
# ============================================
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: chandler-data
namespace: moltbot
labels:
agent: chandler
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: chandler
namespace: moltbot
labels:
agent: chandler
spec:
replicas: 1
selector:
matchLabels:
agent: chandler
template:
metadata:
labels:
agent: chandler
spec:
nodeSelector:
agent: chandler
containers:
- name: moltbot
image: clawdbot/clawdbot:latest
ports:
- containerPort: 18789
name: gateway
- containerPort: 18793
name: canvas
env:
- name: AGENT_NAME
value: "Chandler"
- name: AGENT_ROLE
value: "Marketing & Social Media Manager"
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: anthropic-credentials
key: api-key
- name: CLAWDBOT_GATEWAY_TOKEN
valueFrom:
secretKeyRef:
name: gateway-tokens
key: chandler
- name: CLAWDBOT_GATEWAY_BIND
value: "0.0.0.0"
- name: SLACK_TOKEN
valueFrom:
secretKeyRef:
name: channel-credentials
key: slack-token
volumeMounts:
- name: data
mountPath: /home/node/.openclaw
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: chandler-data
---
apiVersion: v1
kind: Service
metadata:
name: chandler
namespace: moltbot
labels:
agent: chandler
spec:
type: NodePort
selector:
agent: chandler
ports:
- name: gateway
port: 18789
targetPort: 18789
nodePort: 30021
- name: canvas
port: 18793
targetPort: 18793
nodePort: 30022
---
# ============================================
# JOEY - Customer Support Agent (Node 5)
# ============================================
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: joey-data
namespace: moltbot
labels:
agent: joey
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: joey
namespace: moltbot
labels:
agent: joey
spec:
replicas: 1
selector:
matchLabels:
agent: joey
template:
metadata:
labels:
agent: joey
spec:
nodeSelector:
agent: joey
containers:
- name: moltbot
image: clawdbot/clawdbot:latest
ports:
- containerPort: 18789
name: gateway
- containerPort: 18793
name: canvas
env:
- name: AGENT_NAME
value: "Joey"
- name: AGENT_ROLE
value: "Customer Support Specialist"
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: anthropic-credentials
key: api-key
- name: CLAWDBOT_GATEWAY_TOKEN
valueFrom:
secretKeyRef:
name: gateway-tokens
key: joey
- name: CLAWDBOT_GATEWAY_BIND
value: "0.0.0.0"
- name: WHATSAPP_TOKEN
valueFrom:
secretKeyRef:
name: channel-credentials
key: whatsapp-token
volumeMounts:
- name: data
mountPath: /home/node/.openclaw
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: joey-data
---
apiVersion: v1
kind: Service
metadata:
name: joey
namespace: moltbot
labels:
agent: joey
spec:
type: NodePort
selector:
agent: joey
ports:
- name: gateway
port: 18789
targetPort: 18789
nodePort: 30031
- name: canvas
port: 18793
targetPort: 18793
nodePort: 30032
Deploy All Agents¶
# Generate random gateway tokens
export ROSS_TOKEN=$(openssl rand -hex 32)
export RACHEL_TOKEN=$(openssl rand -hex 32)
export CHANDLER_TOKEN=$(openssl rand -hex 32)
export JOEY_TOKEN=$(openssl rand -hex 32)
# Update secrets in the manifest with actual tokens
sed -i "s/GENERATE_RANDOM_TOKEN_1/$ROSS_TOKEN/" moltbot-agents.yaml
sed -i "s/GENERATE_RANDOM_TOKEN_2/$RACHEL_TOKEN/" moltbot-agents.yaml
sed -i "s/GENERATE_RANDOM_TOKEN_3/$CHANDLER_TOKEN/" moltbot-agents.yaml
sed -i "s/GENERATE_RANDOM_TOKEN_4/$JOEY_TOKEN/" moltbot-agents.yaml
# Apply the manifest
kubectl apply -f moltbot-agents.yaml
# Wait for all agents to be ready
kubectl wait --for=condition=Ready pod -l agent=ross -n moltbot --timeout=120s
kubectl wait --for=condition=Ready pod -l agent=rachel -n moltbot --timeout=120s
kubectl wait --for=condition=Ready pod -l agent=chandler -n moltbot --timeout=120s
kubectl wait --for=condition=Ready pod -l agent=joey -n moltbot --timeout=120s
# Verify all running
kubectl get pods -n moltbot -o wide
Onboard Each Agent¶
# Onboard Ross (Development)
kubectl exec -it deployment/ross -n moltbot -- moltbot onboard
# Onboard Rachel (Print Farm)
kubectl exec -it deployment/rachel -n moltbot -- moltbot onboard
# Onboard Chandler (Marketing)
kubectl exec -it deployment/chandler -n moltbot -- moltbot onboard
# Onboard Joey (Support)
kubectl exec -it deployment/joey -n moltbot -- moltbot onboard
Individual Backup Script¶
Save as backup-agent.sh:
#!/bin/bash
# Usage: ./backup-agent.sh <agent-name>
# Example: ./backup-agent.sh ross
set -e
AGENT=${1:-""}
NAMESPACE="moltbot"
BACKUP_DIR="./backups"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
if [ -z "$AGENT" ]; then
echo "Usage: $0 <agent-name>"
echo "Available agents: ross, rachel, chandler, joey"
exit 1
fi
# Validate agent name
if [[ ! "$AGENT" =~ ^(ross|rachel|chandler|joey)$ ]]; then
echo "Error: Unknown agent '$AGENT'"
echo "Available agents: ross, rachel, chandler, joey"
exit 1
fi
echo "=== Backing up $AGENT ==="
# Create backup directory
mkdir -p "$BACKUP_DIR/$AGENT"
# Get pod name
POD_NAME=$(kubectl get pods -n $NAMESPACE -l agent=$AGENT -o jsonpath='{.items[0].metadata.name}')
if [ -z "$POD_NAME" ]; then
echo "Error: No pod found for agent $AGENT"
exit 1
fi
echo "Found pod: $POD_NAME"
# Create backup
BACKUP_FILE="$BACKUP_DIR/$AGENT/$AGENT-$TIMESTAMP.tar.gz"
echo "Creating backup..."
kubectl exec $POD_NAME -n $NAMESPACE -- tar -czvf /tmp/backup.tar.gz \
--exclude='*.sqlite' \
-C /home/node/.openclaw .
echo "Downloading backup..."
kubectl cp $NAMESPACE/$POD_NAME:/tmp/backup.tar.gz "$BACKUP_FILE"
# Cleanup temp file in pod
kubectl exec $POD_NAME -n $NAMESPACE -- rm /tmp/backup.tar.gz
# Show backup info
echo ""
echo "=== Backup Complete ==="
echo "Agent: $AGENT"
echo "File: $BACKUP_FILE"
echo "Size: $(du -h "$BACKUP_FILE" | cut -f1)"
# Keep only last 7 backups per agent
echo ""
echo "Cleaning old backups (keeping last 7)..."
ls -t "$BACKUP_DIR/$AGENT"/*.tar.gz 2>/dev/null | tail -n +8 | xargs -r rm -v
echo "Done!"
Individual Restore Script¶
Save as restore-agent.sh:
#!/bin/bash
# Usage: ./restore-agent.sh <agent-name> <backup-file>
# Example: ./restore-agent.sh ross ./backups/ross/ross-20260130-120000.tar.gz
set -e
AGENT=${1:-""}
BACKUP_FILE=${2:-""}
NAMESPACE="moltbot"
if [ -z "$AGENT" ] || [ -z "$BACKUP_FILE" ]; then
echo "Usage: $0 <agent-name> <backup-file>"
echo "Example: $0 ross ./backups/ross/ross-20260130-120000.tar.gz"
exit 1
fi
# Validate agent name
if [[ ! "$AGENT" =~ ^(ross|rachel|chandler|joey)$ ]]; then
echo "Error: Unknown agent '$AGENT'"
echo "Available agents: ross, rachel, chandler, joey"
exit 1
fi
# Check backup file exists
if [ ! -f "$BACKUP_FILE" ]; then
echo "Error: Backup file not found: $BACKUP_FILE"
exit 1
fi
echo "=== Restoring $AGENT from $BACKUP_FILE ==="
echo ""
echo "WARNING: This will overwrite all data for $AGENT!"
read -p "Are you sure? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "Aborted."
exit 0
fi
# Scale down the agent
echo "Scaling down $AGENT..."
kubectl scale deployment/$AGENT -n $NAMESPACE --replicas=0
kubectl wait --for=delete pod -l agent=$AGENT -n $NAMESPACE --timeout=60s || true
# Create restore pod
echo "Creating restore pod..."
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: restore-$AGENT
namespace: $NAMESPACE
spec:
containers:
- name: restore
image: alpine:latest
command: ["sleep", "3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: $AGENT-data
restartPolicy: Never
EOF
# Wait for restore pod
kubectl wait --for=condition=Ready pod/restore-$AGENT -n $NAMESPACE --timeout=60s
# Clear existing data
echo "Clearing existing data..."
kubectl exec restore-$AGENT -n $NAMESPACE -- sh -c "rm -rf /data/* /data/.*" 2>/dev/null || true
# Copy backup to pod
echo "Uploading backup..."
kubectl cp "$BACKUP_FILE" $NAMESPACE/restore-$AGENT:/tmp/backup.tar.gz
# Extract backup
echo "Extracting backup..."
kubectl exec restore-$AGENT -n $NAMESPACE -- tar -xzvf /tmp/backup.tar.gz -C /data
# Cleanup restore pod
echo "Cleaning up..."
kubectl delete pod restore-$AGENT -n $NAMESPACE
# Scale back up
echo "Starting $AGENT..."
kubectl scale deployment/$AGENT -n $NAMESPACE --replicas=1
kubectl wait --for=condition=Ready pod -l agent=$AGENT -n $NAMESPACE --timeout=120s
# Verify
echo ""
echo "=== Restore Complete ==="
POD_NAME=$(kubectl get pods -n $NAMESPACE -l agent=$AGENT -o jsonpath='{.items[0].metadata.name}')
kubectl exec $POD_NAME -n $NAMESPACE -- moltbot health
echo ""
echo "$AGENT has been restored from $BACKUP_FILE"
Backup All Agents Script¶
Save as backup-all.sh:
#!/bin/bash
# Backup all agents
set -e
AGENTS=("ross" "rachel" "chandler" "joey")
echo "=== Backing up all agents ==="
echo ""
for AGENT in "${AGENTS[@]}"; do
echo "--- $AGENT ---"
./backup-agent.sh $AGENT
echo ""
done
echo "=== All backups complete ==="
ls -la ./backups/*/
Scheduled Backup CronJobs (Individual)¶
# Save as backup-cronjobs.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-ross
namespace: moltbot
spec:
schedule: "0 2 * * *" # 2 AM daily
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: alpine:latest
command: ["/bin/sh", "-c"]
args:
- |
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
cd /data
tar -czvf /backup/ross-$TIMESTAMP.tar.gz --exclude='*.sqlite' .
ls -t /backup/ross-*.tar.gz | tail -n +8 | xargs -r rm
volumeMounts:
- name: data
mountPath: /data
readOnly: true
- name: backup
mountPath: /backup
restartPolicy: OnFailure
nodeSelector:
agent: ross
volumes:
- name: data
persistentVolumeClaim:
claimName: ross-data
- name: backup
persistentVolumeClaim:
claimName: backup-storage
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-rachel
namespace: moltbot
spec:
schedule: "15 2 * * *" # 2:15 AM daily
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: alpine:latest
command: ["/bin/sh", "-c"]
args:
- |
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
cd /data
tar -czvf /backup/rachel-$TIMESTAMP.tar.gz --exclude='*.sqlite' .
ls -t /backup/rachel-*.tar.gz | tail -n +8 | xargs -r rm
volumeMounts:
- name: data
mountPath: /data
readOnly: true
- name: backup
mountPath: /backup
restartPolicy: OnFailure
nodeSelector:
agent: rachel
volumes:
- name: data
persistentVolumeClaim:
claimName: rachel-data
- name: backup
persistentVolumeClaim:
claimName: backup-storage
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-chandler
namespace: moltbot
spec:
schedule: "30 2 * * *" # 2:30 AM daily
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: alpine:latest
command: ["/bin/sh", "-c"]
args:
- |
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
cd /data
tar -czvf /backup/chandler-$TIMESTAMP.tar.gz --exclude='*.sqlite' .
ls -t /backup/chandler-*.tar.gz | tail -n +8 | xargs -r rm
volumeMounts:
- name: data
mountPath: /data
readOnly: true
- name: backup
mountPath: /backup
restartPolicy: OnFailure
nodeSelector:
agent: chandler
volumes:
- name: data
persistentVolumeClaim:
claimName: chandler-data
- name: backup
persistentVolumeClaim:
claimName: backup-storage
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-joey
namespace: moltbot
spec:
schedule: "45 2 * * *" # 2:45 AM daily
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: alpine:latest
command: ["/bin/sh", "-c"]
args:
- |
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
cd /data
tar -czvf /backup/joey-$TIMESTAMP.tar.gz --exclude='*.sqlite' .
ls -t /backup/joey-*.tar.gz | tail -n +8 | xargs -r rm
volumeMounts:
- name: data
mountPath: /data
readOnly: true
- name: backup
mountPath: /backup
restartPolicy: OnFailure
nodeSelector:
agent: joey
volumes:
- name: data
persistentVolumeClaim:
claimName: joey-data
- name: backup
persistentVolumeClaim:
claimName: backup-storage
---
# Shared backup storage PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: backup-storage
namespace: moltbot
spec:
accessModes:
- ReadWriteMany # Needs to be accessible from all nodes
storageClassName: local-path
resources:
requests:
storage: 50Gi
Quick Reference Commands¶
# Check all agents status
kubectl get pods -n moltbot -o wide
# Check specific agent health
kubectl exec deployment/ross -n moltbot -- moltbot health
kubectl exec deployment/rachel -n moltbot -- moltbot health
kubectl exec deployment/chandler -n moltbot -- moltbot health
kubectl exec deployment/joey -n moltbot -- moltbot health
# View logs for specific agent
kubectl logs -f deployment/ross -n moltbot
kubectl logs -f deployment/rachel -n moltbot
kubectl logs -f deployment/chandler -n moltbot
kubectl logs -f deployment/joey -n moltbot
# Shell into specific agent
kubectl exec -it deployment/ross -n moltbot -- /bin/bash
# Restart specific agent
kubectl rollout restart deployment/ross -n moltbot
# List all PVCs
kubectl get pvc -n moltbot
# List all backups (if using CronJobs)
kubectl exec deployment/ross -n moltbot -- ls -la /backup/
# Manual backup
./backup-agent.sh ross
# Restore from backup
./restore-agent.sh ross ./backups/ross/ross-20260130-120000.tar.gz
Service Ports Summary¶
| Agent | Gateway Port | Canvas Port | Access |
|---|---|---|---|
| Ross | 30001 | 30002 | http://pico-node2:30001 |
| Rachel | 30011 | 30012 | http://pico-node3:30011 |
| Chandler | 30021 | 30022 | http://pico-node4:30021 |
| Joey | 30031 | 30032 | http://pico-node5:30031 |
Full Cluster Deployment Strategy¶
The following sections describe the complete multi-node k3s cluster deployment for running multiple specialized agents.
Phase 1: Base OS Installation¶
Recommended OS: Armbian Ubuntu 24.04 Noble Minimal
| Option | Image | Size | Notes |
|---|---|---|---|
| Recommended | Ubuntu 24.04 Noble Minimal | 246 MB | Modern, smallest footprint |
| Alternative | Debian 12 Bookworm Minimal | 250 MB | More stable, longer support |
Why Minimal over Server/Desktop? - Smallest footprint = more RAM for agents (~250 MB vs 1.5 GB) - No desktop environment overhead - k3s handles networking (no Network Manager needed) - Optimized for automation and container workloads
Download page: https://www.armbian.com/rockpro64/
SD Card Preparation (repeat for all 5 cards):
# Download Armbian Minimal for k3s cluster
# Ubuntu 24.04 Noble Minimal (~246 MB) - recommended
wget https://dl.armbian.com/rockpro64/Noble_current_minimal
# Verify integrity
wget https://dl.armbian.com/rockpro64/Noble_current_minimal.sha
sha256sum -c Noble_current_minimal.sha
# Flash to SD card (repeat for each node)
xz -d Armbian_*_Rockpro64_noble_current_*.img.xz
sudo dd if=Armbian_*_Rockpro64_noble_current_*.img of=/dev/sdX bs=4M status=progress
sync
Phase 2: k3s Cluster Setup¶
Node 1 (Server):
# Install k3s server
curl -sfL https://get.k3s.io | sh -s - server \
--write-kubeconfig-mode 644 \
--disable traefik \ # We'll use our own ingress
--flannel-backend=host-gw # Better for bare metal
# Get node token for workers
cat /var/lib/rancher/k3s/server/node-token
Nodes 2-5 (Workers):
# Install k3s agent
curl -sfL https://get.k3s.io | K3S_URL=https://node1:6443 \
K3S_TOKEN="<node-token>" sh -s - agent
Phase 3: OpenClaw Deployment¶
Container Registry:
# Local registry for ARM64 images
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry
namespace: kube-system
spec:
template:
spec:
containers:
- name: registry
image: registry:2
ports:
- containerPort: 5000
OpenClaw Helm Values:
# values-openclaw.yaml
image:
repository: openclaw/openclaw
tag: latest-arm64
pullPolicy: IfNotPresent
agents:
- name: dev-agent
replicas: 1
nodeSelector:
kubernetes.io/hostname: node2
resources:
requests:
memory: '1Gi'
cpu: '500m'
limits:
memory: '2Gi'
cpu: '1'
- name: print-agent
replicas: 1
nodeSelector:
kubernetes.io/hostname: node3
resources:
requests:
memory: '1Gi'
cpu: '500m'
- name: marketing-agent
replicas: 1
nodeSelector:
kubernetes.io/hostname: node4
- name: support-agent
replicas: 1
nodeSelector:
kubernetes.io/hostname: node5
persistence:
enabled: true
storageClass: local-path
size: 20Gi
ingress:
enabled: true
className: traefik
hosts:
- host: agents.local
paths:
- path: /
pathType: Prefix
secrets:
anthropic:
apiKey: '${ANTHROPIC_API_KEY}'
Phase 4: Monitoring Setup¶
# Prometheus + Grafana for ARM64
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: prometheus
namespace: monitoring
spec:
chart: kube-prometheus-stack
repo: https://prometheus-community.github.io/helm-charts
valuesContent: |-
prometheus:
prometheusSpec:
resources:
requests:
memory: "400Mi"
grafana:
resources:
requests:
memory: "100Mi"
SD Card Images and Setup¶
Pre-Built Image Strategy¶
For a clean cluster deployment, I recommend creating a base image that can be flashed to all 5 SD cards, then configuring node-specific settings on first boot.
Master Image Contents¶
| Component | Version | Purpose |
|---|---|---|
| Armbian | 24.5 Bookworm | Base OS |
| k3s | v1.29.x | Container orchestration |
| Docker | 25.x | Container runtime |
| Node.js | 22 LTS | OpenClaw runtime |
| containerd | 1.7.x | k3s container runtime |
Image Creation Script¶
#!/bin/bash
# create-cluster-image.sh
# Start from fresh Armbian installation
apt update && apt upgrade -y
# Install prerequisites
apt install -y \
curl \
git \
ca-certificates \
gnupg \
lsb-release \
open-iscsi \
nfs-common
# Install Docker (for building images locally)
curl -fsSL https://get.docker.com | sh
usermod -aG docker $(whoami)
# Disable swap (required for k3s)
swapoff -a
sed -i '/swap/d' /etc/fstab
# Configure kernel parameters
cat >> /etc/sysctl.conf << EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# First-boot configuration script
cat > /etc/rc.local << 'EOF'
#!/bin/bash
HOSTNAME=$(cat /etc/machine-id | head -c 8)
hostnamectl set-hostname "pico-${HOSTNAME}"
# Check for server vs worker config
if [ -f /boot/k3s-server ]; then
curl -sfL https://get.k3s.io | sh -s - server \
--write-kubeconfig-mode 644 \
--flannel-backend=host-gw
elif [ -f /boot/k3s-worker ]; then
K3S_URL=$(cat /boot/k3s-url)
K3S_TOKEN=$(cat /boot/k3s-token)
curl -sfL https://get.k3s.io | K3S_URL=$K3S_URL K3S_TOKEN=$K3S_TOKEN sh -s - agent
fi
# Remove rc.local after first boot
rm /etc/rc.local
EOF
chmod +x /etc/rc.local
Per-Node Configuration Files¶
Node 1 (Server):
# Create on SD card boot partition
touch /boot/k3s-server
Nodes 2-5 (Workers):
# After Node 1 is running, copy token
touch /boot/k3s-worker
echo "https://192.168.1.100:6443" > /boot/k3s-url
ssh root@node1 'cat /var/lib/rancher/k3s/server/node-token' > /boot/k3s-token
Alternative: Cloud-Init¶
For more sophisticated configuration, use cloud-init:
# /boot/user-data (Node 1 - Server)
#cloud-config
hostname: pico-server
manage_etc_hosts: true
users:
- name: admin
sudo: ALL=(ALL) NOPASSWD:ALL
groups: docker
ssh_authorized_keys:
- ssh-rsa AAAA... your-public-key
runcmd:
- curl -sfL https://get.k3s.io | sh -s - server --write-kubeconfig-mode 644
- kubectl create namespace openclaw
Cost Analysis¶
One-Time Costs¶
| Item | Cost |
|---|---|
| PicoCluster RockPro64 5H | $839.00 |
| Application Cluster Set (640GB) | $340.00 |
| Subtotal Hardware | $1,179.00 |
| Shipping (estimated) | $50.00 |
| Total One-Time | ~$1,229.00 |
Monthly Operating Costs¶
| Item | Cost |
|---|---|
| Claude Opus 4.5 API (estimated) | $465.00 |
| Electricity (~50W cluster @ $0.15/kWh) | $5.40 |
| Internet (existing) | $0.00 |
| Total Monthly | ~$470.00 |
Cost Comparison vs Cloud Alternatives¶
| Solution | Setup Cost | Monthly Cost | 1-Year TCO |
|---|---|---|---|
| PicoCluster (this proposal) | $1,229 | $470 | $6,869 |
| AWS t4g.medium × 5 | $0 | $615 | $7,380 |
| Hetzner CAX21 × 5 | $0 | $230 + API | $5,310 |
| Mac Mini M4 × 2 | $1,200 | $470 | $6,840 |
Note: PicoCluster provides full ownership, no cloud dependency, and educational/hobby value.
Risks and Mitigations¶
Technical Risks¶
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| SD card failure | Medium | High | Use quality cards (Samsung Pro Endurance), regular backups |
| Network partition | Low | High | Implement node health checks, automatic failover |
| Memory exhaustion | Medium | Medium | Configure resource limits, enable swap (2GB per node) |
| API rate limiting | Low | Medium | Implement request queuing, use batch API |
| Power outage | Low | High | UPS recommended, k3s handles ungraceful shutdown |
Operational Risks¶
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| API cost overrun | Medium | Medium | Set usage alerts, implement budget caps |
| Security breach | Low | High | Network isolation, strong auth, regular updates |
| Agent misbehavior | Medium | Low | Sandbox all tools, approval workflows for sensitive actions |
| Dependency on cloud | High | Medium | Cache prompts locally, consider hybrid with local models |
Hardware Reliability¶
RockPro64 boards have mixed reliability reviews:
- SD card slots can be sensitive to card quality
- USB-C power delivery can be inconsistent
- PCIe slot provides upgrade path to NVMe
Recommendation: Keep one spare RockPro64 board for hot-swap in case of failure.
Recommendations¶
Immediate Actions¶
- Order the PicoCluster as configured ($1,179)
- Sign up for Anthropic API access and set up billing
- Download Armbian images and prepare SD cards
Deployment Priorities¶
| Priority | Task | Complexity |
|---|---|---|
| 1 | k3s cluster setup | Medium |
| 2 | Single OpenClaw instance | Low |
| 3 | Multi-agent configuration | Medium |
| 4 | Print farm integration | High |
| 5 | Full production deployment | High |
Alternative Considerations¶
If the PicoCluster proves insufficient:
- Upgrade Path: Add NVMe drives via PCIe for faster storage
- Hybrid Approach: Use cluster for orchestration, offload to cloud for heavy workloads
- Scale Out: Add more RockPro64 nodes to the cluster
Success Criteria¶
| Metric | Target |
|---|---|
| Agent response time | <30 seconds |
| Uptime | 99% |
| API cost per agent | <$150/month |
| Successful task completion | >90% |
Appendices¶
Appendix A: OpenClaw Configuration Reference¶
{
"gateway": {
"bind": "0.0.0.0",
"port": 18789,
"token": "${GATEWAY_TOKEN}"
},
"model": {
"provider": "anthropic",
"model": "claude-opus-4.5",
"apiKey": "${ANTHROPIC_API_KEY}"
},
"agents": {
"defaults": {
"sandbox": {
"mode": "non-main",
"scope": "agent",
"docker": {
"image": "openclaw-sandbox:bookworm-slim",
"memory": "1g",
"cpus": 1,
"network": "none"
}
}
},
"list": []
},
"channels": {
"telegram": {
"enabled": true,
"token": "${TELEGRAM_BOT_TOKEN}"
},
"discord": {
"enabled": true,
"token": "${DISCORD_BOT_TOKEN}"
},
"whatsapp": {
"enabled": true
}
},
"memory": {
"enabled": true,
"compaction": {
"enabled": true,
"threshold": 100000
}
},
"cron": [],
"webhooks": []
}
Appendix B: k3s Cluster Commands¶
# Check cluster status
kubectl get nodes -o wide
# View all pods
kubectl get pods -A
# Check OpenClaw logs
kubectl logs -n openclaw deployment/openclaw-gateway -f
# Scale agents
kubectl scale deployment/dev-agent --replicas=2
# Update configuration
kubectl apply -f openclaw-config.yaml
# Access OpenClaw dashboard
kubectl port-forward svc/openclaw-gateway 18789:18789
Appendix C: Moonraker API Examples¶
import requests
import websocket
import json
class MoonrakerClient:
def __init__(self, host, port=7125):
self.base_url = f"http://{host}:{port}"
self.ws_url = f"ws://{host}:{port}/websocket"
def get_printer_status(self):
response = requests.get(
f"{self.base_url}/printer/objects/query",
params={"print_stats": "state,message,filename,total_duration"}
)
return response.json()
def start_print(self, filename):
response = requests.post(
f"{self.base_url}/printer/print/start",
json={"filename": filename}
)
return response.json()
def cancel_print(self):
response = requests.post(f"{self.base_url}/printer/print/cancel")
return response.json()
Appendix D: n8n Complementary Deployment¶
For additional workflow automation, n8n can be deployed alongside OpenClaw:
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
namespace: automation
spec:
template:
spec:
containers:
- name: n8n
image: n8nio/n8n:latest
ports:
- containerPort: 5678
env:
- name: N8N_BASIC_AUTH_ACTIVE
value: 'true'
- name: N8N_BASIC_AUTH_USER
value: 'admin'
- name: N8N_BASIC_AUTH_PASSWORD
valueFrom:
secretKeyRef:
name: n8n
key: password
volumeMounts:
- name: data
mountPath: /home/node/.n8n
Appendix E: Resource Monitoring Dashboard¶
# Grafana dashboard for cluster monitoring
apiVersion: v1
kind: ConfigMap
metadata:
name: openclaw-dashboard
namespace: monitoring
data:
dashboard.json: |
{
"title": "OpenClaw Cluster",
"panels": [
{
"title": "Agent CPU Usage",
"type": "graph",
"targets": [
{
"expr": "sum(rate(container_cpu_usage_seconds_total{pod=~\".*agent.*\"}[5m])) by (pod)"
}
]
},
{
"title": "Agent Memory Usage",
"type": "graph",
"targets": [
{
"expr": "sum(container_memory_usage_bytes{pod=~\".*agent.*\"}) by (pod)"
}
]
},
{
"title": "API Request Latency",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))"
}
]
}
]
}
Document History¶
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-30 | AI Assistant | Initial research document |
References¶
- OpenClaw Documentation - https://docs.clawd.bot/
- OpenClaw GitHub - https://github.com/openclaw/openclaw
- k3s Documentation - https://docs.k3s.io/
- RockPro64 Wiki - https://pine64.org/documentation/ROCKPro64/
- Anthropic Claude API - https://docs.anthropic.com/
- Claude Agent SDK - https://pypi.org/project/claude-agent-sdk/
- Moonraker API - https://moonraker.readthedocs.io/
- n8n Self-Hosted AI Kit - https://github.com/n8n-io/self-hosted-ai-starter-kit