Skip to content

AI Agent Cluster Feasibility Study: OpenClaw on PicoCluster

Document Version: 1.0
Date: 2026-01-30
Status: Research
Author: AI Assistant


Table of Contents

  1. Executive Summary
  2. Hardware Analysis: PicoCluster RockPro64
  3. OpenClaw: Framework Deep Dive
  4. Cluster Orchestration: k3s vs Docker Swarm
  5. LLM Backend: Claude Opus 4.5 API
  6. Use Case Architectures
  7. MVP: Single Moltbot on k3s ← Start Here
  8. Backup & Restore Strategy
  9. Multi-Agent Deployment: Ross, Rachel, Chandler & Joey
  10. Full Cluster Deployment Strategy
  11. SD Card Images and Setup
  12. Cost Analysis
  13. Risks and Mitigations
  14. Recommendations
  15. 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:

  1. Direct OpenClaw deployment - Deploy OpenClaw directly on k3s (recommended for ARM64 cluster)
  2. 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 file
  • CLAWDBOT_STATE_DIR — per-instance sessions, credentials, caches
  • agents.defaults.workspace — per-instance workspace root
  • gateway.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:

  1. ARM64 Optimization: k3s is specifically designed for ARM and edge devices
  2. Single Binary: <70MB, minimal attack surface
  3. SQLite Backend: No etcd cluster needed for 5 nodes
  4. Traefik Included: Built-in ingress controller
  5. 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

  1. Prompt Caching: Reuse system prompts and common context
  2. Batch Processing: 50% discount for non-real-time workloads
  3. Model Tiering: Use Haiku for simple tasks, Opus for complex reasoning
  4. 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:

  1. Name your agent (e.g., "PrintFarmBot")
  2. AI provider → Already configured via env var (Anthropic)
  3. Model → Select "claude-opus-4.5"
  4. 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

  1. Open Telegram
  2. Find your bot by username
  3. Send: "Hello, what can you do?"
  4. 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:

  1. Add worker nodes - Join nodes 2-5 to the cluster
  2. Add more channels - Discord, WhatsApp, Slack
  3. Create specialized agents - Dev, Print Farm, Support, Marketing
  4. Deploy monitoring - Prometheus + Grafana
  5. 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 WhatsApp

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

  1. Order the PicoCluster as configured ($1,179)
  2. Sign up for Anthropic API access and set up billing
  3. 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:

  1. Upgrade Path: Add NVMe drives via PCIe for faster storage
  2. Hybrid Approach: Use cluster for orchestration, offload to cloud for heavy workloads
  3. 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

  1. OpenClaw Documentation - https://docs.clawd.bot/
  2. OpenClaw GitHub - https://github.com/openclaw/openclaw
  3. k3s Documentation - https://docs.k3s.io/
  4. RockPro64 Wiki - https://pine64.org/documentation/ROCKPro64/
  5. Anthropic Claude API - https://docs.anthropic.com/
  6. Claude Agent SDK - https://pypi.org/project/claude-agent-sdk/
  7. Moonraker API - https://moonraker.readthedocs.io/
  8. n8n Self-Hosted AI Kit - https://github.com/n8n-io/self-hosted-ai-starter-kit