Skip to main content

Deploy ZeroClaw with Lark: Step-by-Step Guide

Published: March 14, 2026 Updated: May 24, 2026 Larry Qu 11 min read

Introduction

ZeroClaw is an open-source, ultra-lightweight AI assistant framework written in Rust. It is designed for efficiency — it can run on edge devices with minimal memory footprint while supporting multiple messaging channels including Telegram, Discord, Slack, Lark, and more. Lark (a workplace productivity superapp by ByteDance, similar to Slack) provides the messaging layer where users interact with the ZeroClaw agent.

This guide walks through deploying ZeroClaw with Lark channel support on Ubuntu 24.04, covering installation, configuration of LLM providers, Lark bot setup, systemd integration for production, and troubleshooting common issues.

Architecture Overview

ZeroClaw’s architecture separates concerns into distinct components that communicate through well-defined interfaces:

flowchart LR
    User[User in Lark Client] -->|Messages via| LarkAPI[Lark API Gateway]
    LarkAPI -->|WebSocket/Webhook| Gateway[ZeroClaw Gateway]
    Gateway --> Daemon[ZeroClaw Daemon]
    Daemon --> Engine[Core Engine]
    Engine --> LLM[LLM Provider<br/>e.g. MiniMax-M2.5]
    Engine --> Tools[Sandboxed Tools]
    Daemon --> Channels[Channel Manager]
    Channels --> LarkAPI

    style LLM fill:#3b82f6,color:#fff
    style Engine fill:#10b981,color:#fff
  • Gateway: Exposes a webhook server that receives incoming messages from channel integrations. Handles TLS termination and request routing.
  • Daemon: The autonomous runtime that manages heartbeat checks, cron jobs, and channel lifecycle. Runs continuously as a background service.
  • Core Engine: Processes messages, invokes the LLM, manages conversation state, and coordinates tool execution.
  • Channels: Adapter layer that normalizes messages from different platforms into a common format. Each channel has its own configuration with allowlist-based access control.
  • Tools: Sandboxed capabilities the agent can invoke (file access, web search, code execution). Each tool is scoped to a workspace for security.

Installation

Prerequisites

  • Ubuntu 24.04 or compatible Linux distribution
  • Rust toolchain (for compiling with Lark feature)
  • A Lark bot application registered in the Lark Developer Console
  • An LLM API key (MiniMax, OpenAI, or Anthropic)

Step 1: Install ZeroClaw

The quickest way to install ZeroClaw is through the official installation script:

curl -fsSL https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/main/scripts/install-release.sh | bash

This script downloads the latest pre-built binary for your architecture and places it in /usr/local/bin/. Verify the installation:

zeroclaw --version

Step 2: Build with Lark Channel Support

The default binary may not include Lark channel support. You need to compile from source with the Lark feature flag enabled:

# Clone the repository (or download the source archive)
git clone https://github.com/zeroclaw-labs/zeroclaw.git
cd zeroclaw

# Build with Lark channel support
cargo build --release --features "channel-lark"

# Find the compiled binary location
which zeroclaw

# Replace the system binary with the Lark-enabled version
cp target/release/zeroclaw /home/username/.cargo/bin/zeroclaw
# Or if installed globally:
sudo cp target/release/zeroclaw /usr/local/bin/zeroclaw

The --features "channel-lark" flag tells the Rust compiler to include the Lark adapter module. Without this flag, the Lark channel configuration section in your config file will be ignored.

Step 3: Initialize the Configuration

ZeroClaw creates its default configuration on first run:

zeroclaw onboard

This generates ~/.zeroclaw/config.toml with default values. You will edit this file in the next steps to add your LLM provider and Lark channel settings.

LLM Provider Configuration

Edit ~/.zeroclaw/config.toml and set your LLM provider. This example uses MiniMax-M2.5:

# ~/.zeroclaw/config.toml — LLM provider settings
api_key = "your-minimax-api-key-here"
default_provider = "minimax-cn"
default_model = "MiniMax-M2.5"

Each field serves a specific purpose:

  • api_key: The API key for your chosen LLM provider. This is read from the config file at startup and used for all LLM requests. Store it securely — consider using environment variables or a secrets manager rather than hardcoding.
  • default_provider: The provider identifier. ZeroClaw supports multiple providers (OpenAI, Anthropic, MiniMax) and routes requests based on this setting.
  • default_model: The specific model to use. For MiniMax, options include MiniMax-M2.5 (latest), MiniMax-M1, or earlier versions. For OpenAI, this would be gpt-4o or gpt-4o-mini.

Lark Channel Configuration

1. Create a Lark Bot

Go to the Lark Developer Console and create a new bot application. After creation, you will receive:

  • App ID: Unique identifier for your bot
  • App Secret: Secret key for API authentication — treat this like a password
  • Verification Token (optional): Used to verify webhook requests originate from Lark
  • Encrypt Key (optional): Used to encrypt/decrypt message payloads

2. Configure the Lark Channel in ZeroClaw

Add the following section to ~/.zeroclaw/config.toml:

[channels_config.lark]
app_id = "cli_a5f3b2c1d4e5"
app_secret = "your-app-secret-here"
encrypt_key = ""                    # optional: set if you enabled encryption in Lark console
verification_token = ""             # optional: set if you enabled verification in Lark console
allowed_users = ["*"]               # allowlist: "*" = all users, or specify Lark user IDs
mention_only = false              # optional: if true, bot only responds to @mentions in groups
use_feishu = false                  # set true if using Feishu (Chinese version) instead of Lark
receive_mode = "websocket"          # "websocket" (recommended) or "webhook"
port = 8081                          # only needed for webhook mode

Key configuration decisions:

  • receive_mode: websocket mode establishes a persistent connection to Lark’s real-time API, eliminating the need for a public-facing webhook endpoint. This is simpler for home servers or NAT’d environments. webhook mode requires a publicly accessible HTTP endpoint and is better for containerized deployments.
  • allowed_users: This is an allowlist — empty by default (deny all). Set to ["*"] for development or specify individual Lark user IDs (["user_123", "user_456"]) for production to restrict access.
  • mention_only: When true, the bot only responds when explicitly @mentioned in group chats. Direct messages always work. This prevents the bot from responding to every message in a busy group.
  • use_feishu: Set to true if your organization uses Feishu (the Chinese mainland version of Lark). The APIs differ slightly between the two platforms.

Production Systemd Service

To ensure ZeroClaw’s Lark channel runs persistently as a background service, create a systemd user service:

# ~/.config/systemd/user/zeroclaw-lark.service
[Unit]
Description=ZeroClaw Lark Channel
After=zeroclaw.service
Requires=zeroclaw.service

[Service]
Type=simple
ExecStart=/home/user/.cargo/bin/zeroclaw channel start
Restart=always
RestartSec=5
Environment="HOME=/home/user"

[Install]
WantedBy=default.target

This service file:

  • Declares a dependency on the main zeroclaw.service via After and Requires, ensuring the daemon starts before the Lark channel.
  • Uses Restart=always with a 5-second delay so the channel automatically recovers from crashes or transient network failures.
  • Sets HOME explicitly because systemd services may not inherit the user’s environment.

Enable and start the service:

systemctl --user daemon-reload
systemctl --user enable zeroclaw-lark
systemctl --user restart zeroclaw-lark

Verification and Monitoring

Check Service Status

systemctl --user status zeroclaw-lark

A healthy service shows active (running) with no recent restarts.

Monitor Real-Time Logs

journalctl --user -u zeroclaw-lark -f

Expected Log Output

When the Lark channel connects successfully, you should see:

2026-03-14T04:18:40.545193Z  INFO zeroclaw::channels::lark: Lark: WS connected

This confirms ZeroClaw established a WebSocket connection to Lark’s real-time API. If you see connection errors instead, check your app_id and app_secret values in the config file.

Verify Bot Responses

Send a message to your Lark bot. In the journal logs, you should see incoming message events and corresponding LLM responses:

2026-03-14T04:20:15.123456Z  INFO zeroclaw::channels::lark: Received message from user_abc123
2026-03-14T04:20:16.789012Z  INFO zeroclaw::engine: LLM response generated (latency: 1.2s)
2026-03-14T04:20:16.890123Z  INFO zeroclaw::channels::lark: Message sent to user_abc123

Adding the Bot to Lark Messenger

  1. Open Lark and search for your bot by the name you set in the Developer Console.
  2. Send a direct message to start a conversation.
  3. For group chats, add the bot as a member. If mention_only is true, @mention the bot to trigger a response.
  4. Test with simple requests: “Summarize this article” or “What is the capital of France?”

Troubleshooting

Symptom Likely Cause Solution
WS connected never appears Incorrect app_id or app_secret Re-check credentials in Lark Developer Console
Connection refused on port 8081 Port conflict or webhook mode without public endpoint Switch to receive_mode = "websocket"
Bot does not respond in groups mention_only = true @mention the bot, or set mention_only = false
LLM returns errors Invalid API key or quota exhausted Verify api_key and check your LLM provider dashboard
Service keeps restarting Config syntax error Run zeroclaw channel start manually to see the error
Messages received but no response Model config issue Check default_provider and default_model match available models

Multiple LLM Provider Configuration

ZeroClaw supports routing between different LLM providers. Configure fallback and priority:

# ~/.zeroclaw/config.toml — Multi-provider setup
[llm]
default_provider = "minimax-cn"
default_model = "MiniMax-M2.5"

[llm.providers.openai]
api_key = "sk-openai-..."
base_url = "https://api.openai.com/v1"
default_model = "gpt-4o"

[llm.providers.anthropic]
api_key = "sk-ant-..."
default_model = "claude-sonnet-4-20260514"

[llm.providers.minimax]
api_key = "your-minimax-key"
base_url = "https://api.minimaxi.com/v1"
default_model = "MiniMax-M2.5"

# Provider fallback chain
[llm.fallback]
enabled = true
providers = ["minimax-cn", "openai", "anthropic"]
timeout_seconds = 30

The fallback chain ensures high availability — if MiniMax is rate-limited or down, ZeroClaw automatically tries OpenAI, then Anthropic. Each provider’s timeout prevents cascading delays.

Provider-Specific Prompt Templates

[llm.prompts]
default = """
You are a helpful AI assistant running in Lark.
Respond concisely and accurately.
"""

technical = """
You are a technical assistant for developers.
Provide code examples and technical explanations.
Format responses with markdown code blocks.
"""

Multi-Channel Configuration

ZeroClaw supports running multiple channels simultaneously. Each channel has independent configuration:

[channels_config.lark]
app_id = "cli_a5f3b2c1d4e5"
app_secret = "..."

[channels_config.telegram]
bot_token = "123456:ABC-DEF..."
allowed_users = ["user_789"]

[channels_config.discord]
bot_token = "discord_bot_token..."
guild_id = "123456789"
allowed_channels = ["general", "ai-bot"]

This enables a unified agent that responds across Lark, Telegram, and Discord from a single ZeroClaw instance. Each channel can have its own allowlist and configuration.

Prometheus Monitoring Integration

For production monitoring, expose ZeroClaw metrics to Prometheus:

# ~/.zeroclaw/config.toml — Monitoring section
[monitoring]
enable_metrics = true
metrics_port = 9090
enable_tracing = false
# prometheus.yml — scrape config
scrape_configs:
  - job_name: 'zeroclaw'
    static_configs:
      - targets: ['localhost:9090']
    metrics_path: '/metrics'

Key metrics exposed:

Metric Type Description
zeroclaw_messages_received_total Counter Total incoming messages
zeroclaw_messages_processed_total Counter Messages successfully processed
zeroclaw_llm_latency_seconds Histogram LLM response latency
zeroclaw_channel_errors_total Counter Channel adapter errors (by channel)
zeroclaw_tool_executions_total Counter Tool call executions
zeroclaw_memory_usage_bytes Gauge Current process memory
zeroclaw_uptime_seconds Counter Service uptime

Grafana Dashboard

Create a monitoring dashboard with these panels:

{
  "title": "ZeroClaw Performance",
  "panels": [
    {
      "title": "Messages Processed",
      "type": "stat",
      "targets": [{"expr": "rate(zeroclaw_messages_processed_total[5m])"}]
    },
    {
      "title": "LLM Latency p95",
      "type": "gauge",
      "targets": [{
        "expr": "histogram_quantile(0.95, rate(zeroclaw_llm_latency_seconds_bucket[5m]))"
      }]
    },
    {
      "title": "Error Rate",
      "type": "timeseries",
      "targets": [{"expr": "rate(zeroclaw_channel_errors_total[5m])"}]
    }
  ]
}

Alerting Rules

# alerts.yml
groups:
  - name: zeroclaw
    rules:
      - alert: HighErrorRate
        expr: rate(zeroclaw_channel_errors_total[5m]) > 0.05
        for: 5m
        labels: { severity: warning }
        annotations:
          summary: "ZeroClaw error rate above 5%"

      - alert: HighLatency
        expr: histogram_quantile(0.95, rate(zeroclaw_llm_latency_seconds_bucket[5m])) > 10
        for: 5m
        labels: { severity: critical }
        annotations:
          summary: "LLM p95 latency above 10 seconds"

      - alert: ServiceDown
        expr: up{job="zeroclaw"} == 0
        for: 1m
        labels: { severity: critical }
        annotations:
          summary: "ZeroClaw service is down"

Custom Tool Development

ZeroClaw supports custom tools that the agent can invoke. Tools are defined as WebAssembly (WASM) modules for sandboxed execution:

// example tool: web-search
use zeroclaw_sdk::{Tool, ToolContext, ToolResult};

#[derive(serde::Deserialize)]
struct SearchInput {
    query: String,
    max_results: Option<u32>,
}

struct WebSearchTool;

#[async_trait]
impl Tool for WebSearchTool {
    fn name(&self) -> &str { "web_search" }
    fn description(&self) -> &str { "Search the web for current information" }

    fn input_schema(&self) -> serde_json::Value {
        serde_json::json!({
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "Search query"},
                "max_results": {"type": "integer", "default": 5}
            },
            "required": ["query"]
        })
    }

    async fn execute(&self, input: ToolInput, ctx: ToolContext) -> ToolResult {
        let params: SearchInput = serde_json::from_value(input.params)?;
        // Implementation: call search API, return formatted results
        Ok(ToolResult {
            output: serde_json::json!({
                "results": [
                    {"title": "Example", "url": "https://example.com", "snippet": "..."}
                ]
            }),
            metadata: None,
        })
    }
}

Configure custom tool paths:

[tools]
dir = "/etc/zeroclaw/tools"
timeout_seconds = 30
sandbox_enabled = true

[tools.registrations]
web_search = { path = "web-search.wasm", enabled = true }
database_query = { path = "db-query.wasm", enabled = true, allowed_domains = ["localhost"] }

Security Hardening

Network Security

# Restrict ZeroClaw to only necessary outbound connections
sudo ufw allow out to api.larksuite.com port 443
sudo ufw allow out to api.minimaxi.com port 443
sudo ufw deny out from any to any  # Default deny all other outbound

# Create dedicated system user for ZeroClaw
sudo useradd -r -s /bin/false -m -d /var/lib/zeroclaw zeroclaw
sudo chown -R zeroclaw:zeroclaw /etc/zeroclaw /var/lib/zeroclaw

Environment Variables for Secrets

Instead of hardcoding secrets in config.toml, use environment variables:

# ~/.zeroclaw/.env
export ZEROCLAW_LARK_APP_SECRET="your-secret-here"
export ZEROCLAW_OPENAI_API_KEY="sk-..."
export ZEROCLAW_MINIMAX_API_KEY="mm-..."

Reference them in config:

# config.toml — using env vars
api_key = "${ZEROCLAW_MINIMAX_API_KEY}"
default_provider = "minimax-cn"

[channels_config.lark]
app_id = "cli_a5f3b2c1d4e5"
app_secret = "${ZEROCLAW_LARK_APP_SECRET}"

Rate Limiting Configuration

[rate_limiting]
enabled = true
messages_per_minute = 30
messages_per_hour_per_user = 100
conversation_timeout_minutes = 60

CI/CD Pipeline for Updates

# .github/workflows/deploy-zeroclaw.yml
name: Deploy ZeroClaw
on:
  push:
    branches: [main]
    paths:
      - 'Cargo.toml'
      - 'src/**'

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build with Lark support
        run: |
          cargo build --release --features "channel-lark"

      - name: Deploy to production
        run: |
          scp target/release/zeroclaw deploy@server:/tmp/
          ssh deploy@server '
            sudo systemctl stop zeroclaw-lark
            sudo cp /tmp/zeroclaw /usr/local/bin/zeroclaw
            sudo systemctl start zeroclaw-lark
            sudo journalctl -u zeroclaw-lark --no-pager -n 10
          '

Performance Tuning

Token Budget Management

[performance]
max_context_tokens = 4096     # Truncate conversation history to this limit
max_response_tokens = 1024    # Maximum tokens in LLM response
conversation_ttl_minutes = 30 # Clear inactive conversations
batch_process = false         # Enable for high-volume async processing

[performance.rate_limits]
tokens_per_minute = 100000
requests_per_minute = 60

Connection Pooling for Lark WebSocket

[channels_config.lark]
reconnect_interval_seconds = 5
max_reconnect_attempts = 0    # 0 = infinite
heartbeat_interval_seconds = 30
message_queue_size = 1000

Tuning these values based on your workload:

  • High-traffic teams: Increase message_queue_size to 5000, reduce heartbeat_interval to 15s
  • Low-resource environments: Reduce max_context_tokens to 2048, increase conversation_ttl to 120
  • Global teams: Increase reconnect_interval to 30s to handle network instability

Backup and Recovery

#!/bin/bash
# backup-zeroclaw.sh
BACKUP_DIR="/var/backups/zeroclaw"
DATE=$(date +%Y%m%d)

# Backup configuration
mkdir -p $BACKUP_DIR/$DATE
cp -r /etc/zeroclaw $BACKUP_DIR/$DATE/

# Backup data directory
tar czf $BACKUP_DIR/$DATE/zeroclaw-data.tar.gz /var/lib/zeroclaw/

# Backup systemd user services
cp ~/.config/systemd/user/zeroclaw*.service $BACKUP_DIR/$DATE/

# Rotate backups older than 30 days
find $BACKUP_DIR -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;

echo "Backup complete: $BACKUP_DIR/$DATE"

Why Use ZeroClaw

ZeroClaw is distinguished by its lightweight footprint. The Rust binary is typically under 15 MB and consumes less than 50 MB of RAM at idle. This makes it practical for deployment on Raspberry Pi, edge gateways, or shared servers where resource efficiency matters. The deny-by-default allowlist model for channels provides security by design — no channel is active until explicitly enabled and configured.

Resources

Comments

👍 Was this article helpful?