Skip to content

how-to

Back up and restore

Back up and restore

What this guide does

Configure backups for an deputyOS device, take an immediate snapshot, schedule a recurring snapshot, list snapshots in your bucket, and restore from a snapshot id. deputyOS uses rclone as the data plane (any rclone-supported remote works: S3, B2, Google Drive, OneDrive, SFTP, …) and a small TOML file at /etc/deputyos/backup.toml plus an rclone.conf as the control plane.

Snapshots cover the active profile’s data_dir — for OpenClaw, Hermes, and Khoj this means chat history, embeddings, vector indexes, and per-skill state. They do not cover provider keys (secrets.env) — those live in your password manager / secret store of record.

Prerequisites

  • A working deputyOS device with the wizard finished.
  • A reachable rclone-supported remote — S3 bucket, B2 bucket, GCS, OneDrive folder, SFTP server, …
  • Either rclone configured during the wizard, or knowledge of the remote’s auth (so you can configure it post-wizard).

The recipe

1. Configure during the wizard’s backup step

The wizard’s backup step (route /wizard/backup — see Reference → APIs → wizard HTTP) prompts for:

  • The rclone-style remote spec — <remote-name>:<bucket-or-path>.
  • Optional retention in days (default: keep all).

The apply step writes:

  • /etc/deputyos/backup.toml (mode 0644):

    [backup]
    remote = "<name>:<bucket>"
    retention_days = 30
  • /etc/deputyos/rclone.conf (mode 0600, owner agent:agent) — the rclone config the wizard collected.

2. Or configure post-wizard

sudo install -m 0644 /dev/null /etc/deputyos/backup.toml
sudo tee /etc/deputyos/backup.toml >/dev/null <<'EOF'
[backup]
remote = "deputyos-bucket:deputyos-snapshots"
retention_days = 30
EOF

# rclone.conf — generated by `rclone config` on a workstation, then copied.
sudo install -m 0600 -o agent -g agent /tmp/rclone.conf /etc/deputyos/rclone.conf

3. Take an immediate snapshot

sudo deputyctl backup now

deputyctl backup now runs:

rclone --config /etc/deputyos/rclone.conf sync \
    <data_dir> <remote>:<bucket>/<host>-<utc-iso>/

The snapshot id is <hostname>-<utc-iso>, filename-safe.

Preview without uploading:

sudo deputyctl backup now --dry-run

4. Schedule a recurring snapshot

sudo deputyctl backup schedule --every 6h    # every 6 hours
sudo deputyctl backup schedule --at 02:00    # daily at 02:00 local

deputyctl backup schedule writes a systemd timer + service:

  • deputyos-backup.timer — OnCalendar=… or OnUnitActiveSec=….
  • deputyos-backup.service — ExecStart=/usr/local/bin/deputyctl backup now.

Inspect and remove:

sudo deputyctl backup schedule --list
sudo deputyctl backup schedule --disable

In dev mode (DEPUTYOS_DEV_OUT=<dir>), the timer/service files are written under <dev-out>/systemd/ and daemon-reload is skipped, so contributors can exercise the wiring without root.

Restoring

1. List snapshots

sudo deputyctl restore list
sudo deputyctl restore list --json

Prints one row per snapshot: id, size, last-modified, plus a human-friendly age (3h ago, 2d ago).

2. Restore a snapshot

sudo deputyctl restore snapshot --snapshot <id> --yes

The restore is atomic with a pre-restore safety copy. The flow:

  1. Stop the active profile (deputyctl down).
  2. Create a safety copy at <data_dir>.pre-restore-<utc-iso>/.
  3. rclone sync <remote>:<bucket>/<id>/ <data_dir>/.
  4. Start the active profile (deputyctl up).

If step 3 fails, the safety copy is renamed back to <data_dir> and the unit restarts on the original data. If step 4 fails, the safety copy is preserved next to the (broken) data dir for forensics.

The safety copy is kept by default — deputyctl factory-reset is the only command that removes it. Sweep periodically:

sudo find /home/agent/.<profile>.pre-restore-* -maxdepth 0 -mtime +14 \
  -exec rm -rf {} +

Verification

# Take a dry-run; rclone reports what *would* be uploaded.
sudo deputyctl backup now --dry-run

# After a real run, list to see your fresh snapshot.
sudo deputyctl restore list

# Round-trip on a test device:
sudo deputyctl backup now
sudo deputyctl restore list
sudo deputyctl restore snapshot --snapshot <id> --yes
sudo deputyctl status                # active profile is back up

Failure modes

FailureSymptomRecovery
rclone not on PATHbackup now exits 127apt install rclone (the bake usually does this; check if the image is degraded)
remote credentials expiredrclone exits with auth errorRe-run rclone config on a workstation; reinstall /etc/deputyos/rclone.conf
destination fullrclone exits with quota errorFree space at the remote; deputyctl backup now is idempotent
network down mid-restorerestore exits non-zero, safety copy survivesPre-restore data is at <data_dir>.pre-restore-<ts>/; mv it back, then investigate
restore from unrelated profiledata dir contents mismatch the running gatewayStop the gateway, restore the right snapshot, restart

Troubleshooting

Snapshot upload silently exits 0 but bucket is empty rclone’s exit code is the source of truth, but your remote may be misconfigured (wrong bucket, wrong region, IAM denies put). Run rclone --config /etc/deputyos/rclone.conf lsd <remote>:<bucket> to confirm the credentials work end-to-end.

Restore puts data back but channels stop working Channel-specific tokens (Telegram, Slack, Matrix) live in /etc/deputyos/<profile>/channels.d/, NOT in the data dir. They are not part of the snapshot. After restore, re-run the wizard’s channels step or copy the env files manually.

Restoring an old snapshot rolls back the upstream agent No — the agent binary is in the image at /opt/deputyos/profiles/<id>/, not in the data dir. The restore only touches data_dir (~/.<id>/). The agent binary stays at the version the current image baked. If the data schema changed incompatibly between versions, the agent’s startup migration handles it (or fails loudly). Test restores in a non-production flow first.

Run backup now before every update Update is staged-only today but a snapshot before deputyctl update --apply is the safest rollback path. Wire it as a pre-update hook if you script updates.

Search

Search index isn't built yet. Run npm run build to generate it (Pagefind writes to dist/pagefind/).