A container can stop at any time, so oc-workers keeps OpenClaw’s state outside it.
A snapshot is a saved copy of the container disk, an archive is a portable backup made by OpenClaw’s backup command, and the replica is the copy of OpenClaw’s databases in R2.
What is saved
| Class | What it is | How it is saved | Target loss |
|---|---|---|---|
| Databases | OpenClaw SQLite: pairing, cron jobs, ingress queue, sessions | Litestream (a SQLite streaming tool) copies each change to R2 about every second | 1 to 3 s |
| Credentials | Channel and login credentials | Written to the Control DO on each change, plus a copy to R2 at most every 15 minutes | Under 1 s typical, 30 s worst |
| Files | Workspace, plugins, media | Disk snapshots, hourly while awake and at each planned stop | 60 min. 0 after a planned stop. |
| Stored files | Files the agent saves to the storage location r2 |
Written to R2 (f/<inst>/) through the replica gate on each save |
0: durable when the save returns |
| Archives | A portable backup of everything | openclaw backup create on a schedule while awake (every 6 hours by default), at maintenance and before upgrades |
6 h for files |
The config is a versioned document in the Control DO (the Durable Object that holds the install’s records), and secrets are Worker secrets. oc-workers copies other SQLite files to R2 about every 15 minutes when they change.
Stored files are outside snapshots and archives. A restore from a snapshot or an archive does not change them, and an archive does not contain them. A deleted file is gone, because R2 keeps no older copy. See File storage.
Make and restore a backup
ochost backup list
ochost backup create --reason "before I change plugins"
ochost backup restore <id>backup createwakes the install.backup restorefirst prints the loss window and asks you to accept it. Use--accept-lossto skip the question.- After each new archive, oc-workers reads it back and prunes old ones. It keeps the newest verified archive, the newest 8, the newest of each of the last 14 days, the newest of each of the last 8 weeks, and archives made before an upgrade or after a rollback for 90 days. It deletes an archive only when a newer verified one exists.
- You can also select Archive now on the Setup tab of the owner console. It wakes the install.
Point-in-time restore of the database
ochost backup restore --sqlite pitr:<time>
ochost backup restore --sqlite archive:<id>--sqlite takes pitr:<time> or archive:<id>. The time is an ISO time or a number of milliseconds.
Replica checks
Each boot writes the replica to a new generation, a fresh path in R2. Once per generation, while the install is awake, oc-workers restores the replica into a scratch database and runs quick_check. A pass marks the generation verified. A failure marks it bad and sends the gen-bad alert. Cleanup keeps the newest verified generation, or the newest complete one if none is verified.
The Setup tab also has Restore drill, which restores into a throw-away instance.
Snapshots
ochost snapshot list
ochost snapshot prune --dry-runsnapshot list shows sleep snapshots and the chain they form. snapshot prune deletes registry tags of old chains. The plan lists only chains older than the live root, and only after one wake from that root worked. Cloudflare has not published a price for snapshot storage. See Cost.
The install never boots empty
If an install has state and the copy in R2 is missing or unreadable, the boot stops rather than start with empty state. In tests with a missing snapshot, an R2 outage and a wiped Control DO, no empty boot happened. After a Control DO wipe, one test rebuilt the install in 69.3 s.
Measured loss after a crash
These results come from 50 random SIGKILLs on a Cloudflare install with test timers.
| Class | Target | Measured | Result |
|---|---|---|---|
| Databases | 3 s | p50 0.66 s, p95 1.32 s, max 5.95 s. 1 of 49 kills was over 3 s (a kill during an online archive). | Does not hold fully |
| Credentials | 30 s | p50 1.5 s, max 6.9 s | Holds for crashes. If the Control DO is lost, the bound is the 15 minute copy. |
| Other SQLite files | 15 min | p50 188 s, max 1607 s. 5 of 49 were over 15 minutes. | Does not hold |
| Files | 60 min | Max 102.7 s with a checkpoint in the awake period, 1608 s without | Not bounded under crash loops. 0 after a planned stop. |
| Archive | 6 h | Newest archive 12.1 min old at test cadence | Holds at test timers |
| Webhook inbox | 0 | 0 rows lost in 586 acknowledged updates | Holds for rows |
Replies are a separate case. In one test, 4 of 586 acknowledged updates never got a reply after a crash, and a reply can also repeat. Treat replies as at-least-once, not exactly-once.
oc-workers made these changes after the test, and we did not repeat it:
- An online archive no longer reads the live database; it is made from a copy. oc-workers restores the managed databases from the R2 replica and copies other SQLite files with the backup API. This targets the 5.95 s case.
- oc-workers now sees and copies a database change that only touches the WAL file (SQLite’s write-ahead log).
Next
Go to Upgrades.