Containerized OpenClaw agent — immutable, disposable, replaceable. Run a single-purpose AI agent in a container with automatic backup/restore of all state. See the blog post for the philosophy behind the cattle approach.
- OpenClaw agent running in a container
- Telegram as the control channel
- Gradient (DigitalOcean AI) as the model provider
- Automatic backup/restore of agent workspace, sessions, and credentials
- Local dev with Docker Compose + RustFS (local S3)
- Cloud deploy to DigitalOcean App Platform
- Docker + Docker Compose
- A Telegram bot token (via @BotFather)
- A Gradient API key (via DigitalOcean)
-
Clone the repo:
git clone https://github.com/bikramkgupta/openclaw-cattle-mode.git cd openclaw-cattle-mode -
Copy
.env.docker.exampleto.env.dockerand fill in your secrets:cp .env.docker.example .env.docker
Required values:
TELEGRAM_BOT_TOKEN,OPENCLAW_GATEWAY_TOKEN, and at least one provider key (GRADIENT_API_KEY,ANTHROPIC_API_KEY, orOPENAI_API_KEY)Generate a gateway token:
openssl rand -hex 32
-
Start everything:
docker compose --env-file .env.docker up -d
-
Send a message to your Telegram bot. The agent will respond.
Docker Compose starts four services:
- openclaw-agent — the AI agent (built from
openclaw-agent-image/) - rustfs — local S3-compatible store for backup/restore
- rustfs-bucket — creates the default bucket on first run
- rustfs-init — sets volume permissions
bash scripts/setup-spaces.shOr manually: create a Spaces bucket in the DigitalOcean console and generate access keys under API > Spaces Keys.
cp .env.remote.example .env.remote
# Fill in all values (see DEPLOYMENT.md for details)bash scripts/deploy.shThe deploy script reads .env.remote, substitutes values into app.yaml, deploys via doctl, and cleans up. See DEPLOYMENT.md for the full deployment reference.
Preview without deploying:
bash scripts/deploy.sh --dry-runSpaces (S3) is the brain. The container is just bones.
The agent's entire runtime state is continuously synced to a Spaces bucket. When the container restarts or redeploys, state is restored from Spaces. The container itself is disposable.
| Directory | Contents |
|---|---|
2026.3.2 |
2026.3.2 |
2026.3.1 |
2026.3.1 |
2026.2.26 |
2026.2.26 |
2026.2.25 |
2026.2.25 |
2026.2.24 |
2026.2.24 |
2026.2.23 |
2026.2.23 |
2026.2.22-2 |
2026.2.22-2 |
workspace/ |
AGENTS.md, SOUL.md, USER.md, IDENTITY.md, memory/, MEMORY.md, and everything else the agent creates |
agents/ |
Session transcripts, auth profiles, model registry — for all agent IDs (supports multi-agent) |
credentials/ |
OAuth tokens, API keys |
| Directory | Why |
|---|---|
2026.3.2 |
2026.3.2 |
2026.3.1 |
2026.3.1 |
2026.2.26 |
2026.2.26 |
2026.2.25 |
2026.2.25 |
2026.2.24 |
2026.2.24 |
2026.2.23 |
2026.2.23 |
2026.2.22-2 |
2026.2.22-2 |
openclaw.json |
Rendered from environment variables at boot — ephemeral |
- First boot (Spaces is empty): seed files from the image populate the workspace. The agent bootstraps and the backup watcher syncs everything to Spaces.
- Every subsequent boot: the entrypoint restores
workspace/,agents/, andcredentials/from Spaces before starting the gateway. Seed files in the image are ignored — Spaces is the source of truth. - While running: a periodic sync runs every 60 seconds, pushing changes to Spaces.
- On shutdown: a final backup runs before the container exits.
Because Spaces is the source of truth, you update workspace files by pushing them to Spaces — not by rebuilding the image.
# Upload a single file (e.g., updated AGENTS.md)
bash scripts/push-workspace.sh AGENTS.md
# Upload an entire directory
bash scripts/push-workspace.sh workspace/
# List current workspace files in Spaces
bash scripts/push-workspace.sh --listChanges take effect on the next container restart or redeploy.
-
Rebuild the image:
Run the "Build and Push to GHCR" workflow in GitHub Actions.
-
Set the deploy tag in
.env.remote:IMAGE_TAG=2026.2.22-2(or the version you built). This controls which image tag App Platform pulls. -
Redeploy:
bash scripts/deploy.sh
| OpenClaw Version | Image Tag | Status | Notes |
|---|---|---|---|
2026.3.2 |
2026.3.2 |
Current | — |
2026.3.1 |
2026.3.1 |
Tested | — |
2026.2.26 |
2026.2.26 |
Tested | — |
2026.2.25 |
2026.2.25 |
Tested | — |
2026.2.24 |
2026.2.24 |
Tested | — |
2026.2.23 |
2026.2.23 |
Tested | — |
2026.2.22-2 |
2026.2.22-2 |
Tested | — |
2026.2.17 |
2026.2.17 |
Tested | — |
2026.2.15 |
2026.2.15 |
Tested | — |
2026.2.14 |
2026.2.14 |
Tested | — |
2026.2.13 |
2026.2.13 |
Tested | — |
2026.2.12 |
2026.2.12 |
Tested | — |
2026.2.9 |
2026.2.9 |
Tested | Telegram plugin fix auto-applied |
2026.2.6 |
2026.2.6 |
Tested | Previous stable release |
2026.2.3 |
2026.2.3 |
Tested | — |
2026.2.2 |
2026.2.2 |
Tested | — |
2026.2.1 |
2026.2.1 |
Tested | Earliest supported |
The IMAGE_TAG in .env.remote / .env.docker must match an image that has been built and pushed to GHCR. Use the GHCR build workflow to build a new version:
gh workflow run ghcr-build-push.yml -f openclaw_version=<version>The agent runs on a 1cpu/1gb instance. Here's how the ~1024MB is used:
| Component | Typical RSS | Notes |
|---|---|---|
2026.3.2 |
2026.3.2 |
Current |
2026.3.1 |
2026.3.1 |
Tested |
2026.2.26 |
2026.2.26 |
Tested |
2026.2.25 |
2026.2.25 |
Tested |
2026.2.24 |
2026.2.24 |
Tested |
2026.2.23 |
2026.2.23 |
Tested |
2026.2.22-2 |
2026.2.22-2 |
Tested |
openclaw (CLI parent) |
~130 MB | Spawns and supervises the gateway |
openclaw-gateway |
~470 MB | V8 heap + WebSocket + Telegram polling |
| OS + Node runtime | ~50 MB | Shared libs, kernel buffers |
| Available headroom | ~370 MB | For spikes during doctor, subagents, tool calls |
The V8 heap limit is set via NODE_OPTIONS=--max-old-space-size=<MB>:
| Instance | Recommended | Why |
|---|---|---|
2026.3.2 |
2026.3.2 |
Current |
2026.3.1 |
2026.3.1 |
Tested |
2026.2.26 |
2026.2.26 |
Tested |
2026.2.25 |
2026.2.25 |
Tested |
2026.2.24 |
2026.2.24 |
Tested |
2026.2.23 |
2026.2.23 |
Tested |
2026.2.22-2 |
2026.2.22-2 |
Tested |
| 1cpu/1gb | 768 |
512 OOMs during openclaw doctor; 768 leaves room for gateway |
| 1cpu/2gb | 1536 |
Comfortable margin for subagents and large tool outputs |
- Long-running sessions accumulating context (no automatic eviction)
- Multiple concurrent subagents (each holds its own context window)
- Large tool outputs (file reads, web fetches) buffered in memory
openclaw doctorat startup (briefly spikes memory)
The daily gateway restart (see Architecture) mitigates slow leaks.
| File | Purpose |
|---|---|
2026.3.2 |
2026.3.2 |
2026.3.1 |
2026.3.1 |
2026.2.26 |
2026.2.26 |
2026.2.25 |
2026.2.25 |
2026.2.24 |
2026.2.24 |
2026.2.23 |
2026.2.23 |
2026.2.22-2 |
2026.2.22-2 |
.env.docker.example |
Template for local Docker Compose dev |
.env.remote.example |
Template for App Platform deploy |
See DEPLOYMENT.md for the full rules on env files and deployment methods.
All tests except the deploy-spec check run locally (Docker + Docker Compose). CI only runs the deploy-spec test (no Docker in GitHub Actions).
| Test | Command | Where |
|---|---|---|
2026.3.2 |
2026.3.2 |
Current |
2026.3.1 |
2026.3.1 |
Tested |
2026.2.26 |
2026.2.26 |
Tested |
2026.2.25 |
2026.2.25 |
Tested |
2026.2.24 |
2026.2.24 |
Tested |
2026.2.23 |
2026.2.23 |
Tested |
2026.2.22-2 |
2026.2.22-2 |
Tested |
| Container boot (smoke) | bash scripts/smoke-boot.sh |
Local |
| Backup and restore | bash scripts/test-backup.sh |
Local |
| Deploy spec render | bash scripts/test-deploy-spec.sh |
Local or CI |
| Version compatibility | bash scripts/test-versions.sh |
Local |
Run all locally (in order):
bash scripts/smoke-boot.sh && \
bash scripts/test-deploy-spec.sh && \
bash scripts/test-backup.sh && \
bash scripts/test-versions.shWhatsApp uses QR-based linking (no bot token). Three steps:
1. Add your phone number to .env.remote and deploy:
# In .env.remote, set your number (E.164 format):
WHATSAPP_ALLOWFROM=+14085551234
# Deploy
bash scripts/deploy.shThe entrypoint auto-configures channels.whatsapp with dmPolicy: "allowlist" and selfChatMode: true. Only your number can talk to the bot.
2. Connect to the container and scan the QR code:
doctl apps console <app-id> openclaw-agent
# Inside the container:
openclaw channels loginScan the QR from your phone: WhatsApp → Settings → Linked Devices → Link a Device.
3. If the gateway doesn't pick up the connection, restart the container:
bash scripts/deploy.shCredentials are persisted to ~/.openclaw/credentials/whatsapp/ and backed up to S3 automatically. On subsequent restarts, WhatsApp reconnects without scanning again.
The container is built on node:24-bookworm-slim with OpenClaw installed via npm. At startup, the entrypoint script:
- Generates
openclaw.jsonfrom environment variables - Configures the Gradient provider, Telegram, and WhatsApp channels
- Restores workspace, sessions, and credentials from S3-compatible storage
- Runs
openclaw doctorfor migrations (skipped on same-version reboots) - Re-asserts Telegram plugin state (2026.2.9+ fix, only after doctor)
- Installs skills from ClawHub (skipped if already present from S3 restore)
- Starts the gateway + backup watcher
If the container dies, nothing is lost — rebuild, inject the same env vars, and the agent comes back with full state intact.
See the blog post for the detailed writeup.