Hermes/OpenClaw Backblaze Backup And Restore¶
This runbook covers the offsite backup that protects the OpenClaw host state and the Hermes Docker instance used by Skitter. Use it when the openclaw-workspace VM is lost, the Docker host is rebuilt, or /home/openclaw/.hermes is deleted.
What is backed up¶
The daily swarmhosts-openclaw-backup.timer systemd timer runs infra/openclaw/scripts/backup-to-backblaze.sh on openclaw-workspace.
Each archive includes:
/home/openclaw/.local/share/swarmhosts-openclaw— OpenClaw runtime state./home/openclaw/.hermes— Hermes data mounted into the Docker containers as/opt/data, including sessions, memories, skills, cron jobs, gateway state, auth/config, and logs. Rebuildable Hermes cache directories are excluded./data/repos/ai-agent-swarm-hosts/infra/openclaw— the OpenClaw deployment scripts and systemd units./etc/docker/daemon.jsonand/etc/containerd/config.tomlwhen present.
Local archives are written to /data/backups/openclaw, with latest.tar.gz pointing at the newest archive. Backblaze B2 receives the same archive under the swarmhosts-openclaw/ prefix plus swarmhosts-openclaw/latest.json.
Retention defaults:
- local:
OPENCLAW_BACKUP_RETENTION_COUNT=14 - Backblaze B2:
OPENCLAW_B2_RETENTION_COUNT=30
The bucket currently used by the timer is swarm-hosts-backup.
Daily backup verification¶
On the OpenClaw host:
systemctl is-enabled swarmhosts-openclaw-backup.timer
systemctl is-active swarmhosts-openclaw-backup.timer
systemctl list-timers --all 'swarmhosts-openclaw-backup.timer'
journalctl -u swarmhosts-openclaw-backup.service -n 120 --no-pager
A successful run prints JSON similar to:
{
"ok": true,
"bucket": "swarm-hosts-backup",
"latestMarker": "swarmhosts-openclaw/latest.json",
"uploaded": "swarmhosts-openclaw/openclaw-YYYYMMDDTHHMMSSZ.tar.gz"
}
Confirm the latest local archive contains Hermes data without extracting it:
tar -tzf /data/backups/openclaw/latest.tar.gz \
| grep -E '^(home/openclaw/\.hermes/(state\.db|cron/jobs\.json|memories/|skills/)|home/openclaw/\.local/share/swarmhosts-openclaw/)' \
| head -40
Manual backup run¶
Use this after changing the backup script or before risky host work:
sudo systemctl start swarmhosts-openclaw-backup.service
journalctl -u swarmhosts-openclaw-backup.service -n 120 --no-pager
If you only need a local archive without uploading to Backblaze:
sudo -u openclaw OPENCLAW_BACKUP_DIR=/data/backups/openclaw \
HERMES_BACKUP_DIR=/home/openclaw/.hermes \
/data/repos/ai-agent-swarm-hosts/infra/openclaw/scripts/backup.sh
Restore from a local archive¶
Prefer restoring onto a fresh VM with Docker/containerd installed and the openclaw user created. Restoring over a running host is riskier because it can mix old and new Hermes state.
- Stop Hermes/OpenClaw containers and timers:
bash
sudo systemctl stop swarmhosts-openclaw-backup.timer || true
docker stop swarmhosts-hermes-gateway swarmhosts-hermes-dashboard || true
- Preserve any current broken state before overwriting it:
bash
sudo mv /home/openclaw/.hermes /home/openclaw/.hermes.pre-restore.$(date -u +%Y%m%dT%H%M%SZ) 2>/dev/null || true
sudo mv /home/openclaw/.local/share/swarmhosts-openclaw /home/openclaw/.local/share/swarmhosts-openclaw.pre-restore.$(date -u +%Y%m%dT%H%M%SZ) 2>/dev/null || true
- Extract the archive from
/:
bash
sudo tar -C / -xzf /data/backups/openclaw/latest.tar.gz
sudo chown -R openclaw:openclaw /home/openclaw/.hermes /home/openclaw/.local/share/swarmhosts-openclaw
- Reinstall or reload the backup timer if needed:
bash
sudo cp /data/repos/ai-agent-swarm-hosts/infra/openclaw/systemd/swarmhosts-openclaw-backup.* /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now swarmhosts-openclaw-backup.timer
- Restart Hermes from the Hermes repo/compose setup used on the host:
bash
cd /home/openclaw/repos/hermes-agent
docker compose up -d swarmhosts-hermes-gateway swarmhosts-hermes-dashboard
docker ps --filter 'name=swarmhosts-hermes'
- Verify Hermes state came back:
bash
test -s /home/openclaw/.hermes/state.db
test -s /home/openclaw/.hermes/cron/jobs.json
docker logs --tail=100 swarmhosts-hermes-gateway
docker logs --tail=100 swarmhosts-hermes-dashboard
Restore from Backblaze B2¶
If /data/backups/openclaw is gone too, download the latest archive from Backblaze B2 first, then follow the local restore steps above.
The backup uploader reads Backblaze credentials from the private secret file configured by OPENCLAW_B2_SECRET_FILE; do not commit or print that file. The file must contain the bucket name/id plus application key id/key accepted by infra/openclaw/scripts/upload_backblaze_b2.py.
Using the Backblaze web console:
- Open bucket
swarm-hosts-backup. - Download
swarmhosts-openclaw/latest.json. - Read its
archivefield. - Download that object, for example
swarmhosts-openclaw/openclaw-YYYYMMDDTHHMMSSZ.tar.gz, to/data/backups/openclaw/on the rebuilt host. - Set permissions:
bash
sudo mkdir -p /data/backups/openclaw
sudo chown -R openclaw:openclaw /data/backups/openclaw
sudo chmod 700 /data/backups/openclaw
sudo -u openclaw ln -sfn openclaw-YYYYMMDDTHHMMSSZ.tar.gz /data/backups/openclaw/latest.tar.gz
Then run the restore procedure above.
Notes and pitfalls¶
- The archive contains secrets from
/home/openclaw/.hermes; keep local archives mode0600, the backup directory mode0700, and B2 bucket access restricted. - Live Hermes SQLite/log files can change during tar. The backup script allows GNU tar's
file changed as we read itstatus because the archive is still usable and the next daily backup will refresh any hot files. - If the restore target uses different UID/GID values, run
sudo chown -R openclaw:openclawafter extraction before starting containers. - Do not restore
/home/openclaw/.hermeswhile the Hermes containers are running.