Why This Matters
My AI agent has been running for over a week now. He’s learned my projects, how I communicate, and even about my family. He knows which tasks to pick up overnight while I sleep and which ones need my sign-off first. He coordinates with other agents, manages our kanban board, drafts technical docs. He’s become a genuine co-worker and digital butler — one I’d actually miss.
And the personality that he’s developed and his understanding of how I work lives in a workspace folder on a single machine.
I’m not being dramatic. If that disk fails, there’s no re-hiring process — there’s a cold start. A brand new instance with no memory of what we built together. Every decision we made, every lesson learned, every shortcut we developed through working side by side — gone.
That thought bothered me enough to spend an evening building proper backup infrastructure. But first, I had to decide where to run this thing.
Why a Headless Container, Not My Desktop
Early on, I made a deliberate choice: OpenClaw (formerly Clawdbot and Moltbot) runs in an isolated container on a dedicated host, not on my laptop or workstation.
The reason is straightforward. An AI agent that manages your projects, reads your messages, and coordinates your work accumulates context fast — personal information, access tokens, communication patterns. Running that on your daily driver means the agent shares your identity surface. Your browser sessions, your SSH keys, your credentials — all within reach of a process you’re still learning to trust.
We’re not there yet. The identity and access models for autonomous agents are immature at best. There’s no widely adopted standard for scoped agent privileges, no robust framework for limiting what an agent can touch versus what it can see. Until that work is done — and it’s some of the most important work in this space — I’d rather keep my agent in a box with clear boundaries. It gets what it needs and nothing more.
A headless container also forces discipline. No GUI crutches. Everything the agent does is through CLI tools, APIs, and files — which means everything is scriptable, auditable, and reproducible.
GitHub as a Structured Workspace
Running headless has another benefit: it pushes you toward proper structure early.
For a desktop agent, it’s tempting to let artefacts sprawl — files in Downloads, notes on the desktop, half-finished scripts in random directories. A headless agent doesn’t have that luxury. Everything lives in the workspace, and the workspace needs to be organised.
GitHub becomes invaluable here. Not just as a backup target, but as a structured layer on top of the workspace. Project artefacts, documentation, even OpenClaw configuration changes — all versioned, browsable, and reviewable from any device. For a headless setup where you can’t just open a folder and scroll through files, having a GitHub repo you can browse from your phone or laptop is a genuine quality-of-life improvement.
It’s also another storage layer. R2 holds encrypted snapshots for disaster recovery. GitHub holds the living, versioned state. Between the two, you’d have to try pretty hard to lose anything.
The Backup Stack
With that context, here’s what we built. Three tools, each doing one thing well:
- Cloudflare R2 — S3-compatible object storage with zero egress fees.
- rclone — Mature, battle-tested file sync. Supports 70+ backends. The kind of tool that just works until you need it to do something weird, and then it still works.
- age — Modern file encryption. No GPG key management hell. Public key encrypts, private key decrypts, done.
Setting Up rclone with R2
R2 speaks S3, which means rclone works out of the box — once you get the endpoint format right.
[r2-backup]
type = s3
provider = Cloudflare
access_key_id = your_access_key_here
secret_access_key = your_secret_key_here
acl = private
endpoint = https://<ACCOUNT_ID>.r2.cloudflarestorage.com
The endpoint URL is the one thing people get wrong. No trailing slash — just https://<account-id>.r2.cloudflarestorage.com. Your account ID is in the R2 dashboard.
Create your bucket in the Cloudflare dashboard first. Then verify:
rclone lsd r2-backup:
If you see your bucket, you’re in business.
Encryption with age
We chose age over rclone’s built-in crypt layer for one reason: key separation.
The backup process only needs the public key. The agent running nightly backups can encrypt but never decrypt. If the host is compromised, the attacker gets encrypted blobs they can’t read. The private key lives elsewhere — a password manager, a hardware key, wherever you keep things that matter.
Generate a keypair:
age-keygen -o backup-key.txt
This gives you a public key (starts with age1...) and a secret key. Store that secret key somewhere safe and separate from your infrastructure. It’s the only way back in.
The Backup Script
Nothing clever here. Compress, encrypt, upload.
#!/bin/bash
set -euo pipefail
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
WORKSPACE="$HOME/.openclaw/workspace"
DEST="r2-backup:your-bucket/workspace"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
# Compress
tar -czf "$TMPDIR/workspace-${TIMESTAMP}.tar.gz" \
-C "$(dirname "$WORKSPACE")" "$(basename "$WORKSPACE")"
# Encrypt (public key only)
age -r "age1your-public-key-here" \
-o "$TMPDIR/workspace-${TIMESTAMP}.tar.gz.age" \
"$TMPDIR/workspace-${TIMESTAMP}.tar.gz"
# Upload
rclone copy "$TMPDIR/workspace-${TIMESTAMP}.tar.gz.age" "${DEST}/"
Schedule it with cron. Ours runs at 3am.
Restoration
The reverse path. Download, decrypt, extract.
# Download
rclone copy r2-backup:your-bucket/workspace/workspace-20260209-030000.tar.gz.age .
# Decrypt (requires the secret key)
age -d -i /path/to/secret-key.txt workspace-20260209-030000.tar.gz.age > workspace.tar.gz
# Extract
tar -xzf workspace.tar.gz
Test this. A backup you’ve never restored is a hypothesis, not a strategy.
Cost
Effectively zero. R2’s free tier covers 10GB of storage and 10 million reads per month. Our daily backups don’t come close. And because there are no egress fees, restores are free, too.
Design Decisions
Why not rclone crypt? It works fine, but the encryption keys live on the same machine doing the backups. If someone roots the box, they have the keys and the data. With age, the private key is physically elsewhere.
Why not GPG? Life’s too short for GPG key management. age does the same job with a fraction of the complexity.
Why R2 over S3/B2/GCS? Zero egress. I was already having OpenClaw shipping apps to Cloudflare’s developer platform and few prompts were required to add backups to R2.
Takeaways
- Separate encryption keys from backup infrastructure. The system that creates backups should not be able to read them.
- Use tools that do one thing well. rclone moves files. age encrypts them. Composability beats monoliths.
- Test restores regularly. The backup isn’t the product. The restore is.
What This Doesn’t Solve
I want to be honest: this setup protects against data loss, not against everything.
If the agent’s credentials are compromised, encryption doesn’t help — the attacker has live access, not just backups. If I misconfigure the container’s network access, isolation is theatre. If the age secret key is lost, every backup becomes a paperweight. And if the agent itself behaves in ways I don’t expect — well, I’m trusting a process I can audit after the fact, not one I can fully predict.
No solution is risk-free. This is a meaningful step, not a finish line.
A Question Worth Asking
We’re giving these agents our context, our projects, our communication patterns — and increasingly, our trust. We’re building working relationships with them.
So who’s responsible when that trust is misplaced? The developer who built the framework? The person who gave the agent access? The agent itself?
We don’t have good answers yet. But we’d better start asking.
Disclosure: I work at Cloudflare at the time of authoring this post. I chose R2 because I know the platform and think it’s great.
Built on OpenClaw, an open-source AI agent framework. The agent manages its own backups autonomously — it just needed the right tools.