openclaw-backup-restore
Backup and restore OpenClaw configuration, agents, sessions, and workspace to/from a private Git repository. Use when the user wants to manually trigger a backup, migrate to a new machine, or restore from a previous state.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-openclaw-backup-restore
Repository
Skill path: skills/darinrowe/openclaw-backup-restore
Backup and restore OpenClaw configuration, agents, sessions, and workspace to/from a private Git repository. Use when the user wants to manually trigger a backup, migrate to a new machine, or restore from a previous state.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install openclaw-backup-restore into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding openclaw-backup-restore to shared team environments
- Use openclaw-backup-restore for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
--- name: openclaw-backup-restore description: Backup and restore OpenClaw configuration, agents, sessions, and workspace to/from a private Git repository. Use when the user wants to manually trigger a backup, migrate to a new machine, or restore from a previous state. --- # OpenClaw Backup & Restore A specialized skill for managing the lifecycle of your OpenClaw data. This skill utilizes an external Git-managed backup directory to keep your production environment clean while ensuring full recoverability. ## Strategy 1. **Isolation**: Git operations happen in a dedicated directory outside the live `.openclaw` runtime to avoid pollution. 2. **Minimalism**: Large `node_modules`, logs, and temporary files are excluded. 3. **Redundancy**: Regular backups can be scheduled via Cron. --- ## Setup Before using this skill, you must set your private backup repository URL in `openclaw.json`. This URL is used by the scripts to push and pull data. ```bash openclaw config set skills.entries.openclaw-backup-restore.env.OPENCLAW_BACKUP_REPO "[email protected]:your-username/your-repo.git" ``` ## How to Backup To trigger a manual backup and sync to your remote repository: 1. The agent should execute the `backup.sh` script located within this skill's `scripts/` directory. 2. The script will: - Read the repo URL from the OpenClaw config. - Sync `${HOME}/.openclaw/` to `${HOME}/openclaw-backup/` using `rsync` (respecting `.gitignore`). - Generate a readable commit summary from changed paths (for example workspace/config/runtime/memory). - Commit and push to the remote `main` branch. **Trigger Phrases**: "Backup OpenClaw now", "Sync my data to GitHub". --- ## How to Restore To restore your environment on a new or existing machine: 1. Ensure your SSH key is added to your Git provider (e.g., GitHub). 2. The agent should execute the `restore.sh` script located within this skill's `scripts/` directory. 3. The process involves: - Reading the repo URL from the OpenClaw config. - Cloning or pulling the latest backup from the configured repository. - Syncing files back to `${HOME}/.openclaw/`. - Reinstalling node dependencies and running `openclaw doctor --yes` to fix environment paths. 4. **Restart the Gateway**: `openclaw gateway restart`. **Trigger Phrases**: "Restore OpenClaw from backup", "Migrate my data". --- ## Technical Details - **Backup Directory**: `${HOME}/openclaw-backup` - **Source Directory**: `${HOME}/.openclaw` - **Exclusions**: Defined in the skill's `.gitignore` (includes `node_modules/`, `logs/`, `completions/`, `tmp/`, `dist/`). - **Automatic Setup**: The `.gitignore` file is included in this skill and will be copied to `${HOME}/openclaw-backup/` during the first backup run. --- ## Recovery Checklist If restoring to a **completely new machine**: 1. Install OpenClaw CLI first. 2. Set the `OPENCLAW_BACKUP_REPO` config value. 3. Configure SSH access for your Git provider. 4. Run the restore script provided by this skill. 5. Run `openclaw onboard` if you need to re-install the daemon service. --- ## Skill Companion Files > Additional files collected from the skill directory layout. ### _meta.json ```json { "owner": "darinrowe", "slug": "openclaw-backup-restore", "displayName": "OpenClaw Backup & Restore", "latest": { "version": "1.0.3", "publishedAt": 1773546292889, "commit": "https://github.com/openclaw/skills/commit/ebe5501a89119236426e981dd069f1a2742359fb" }, "history": [ { "version": "1.0.2", "publishedAt": 1773369985150, "commit": "https://github.com/openclaw/skills/commit/55dac59d9390d3333c1549e8c02a204fdea208f0" }, { "version": "1.0.0", "publishedAt": 1772696981216, "commit": "https://github.com/openclaw/skills/commit/9bec765c90ef35da0f839f03fc315442a8664333" } ] } ``` ### scripts/backup.sh ```bash #!/usr/bin/env bash # OpenClaw Backup Script ☺️🌸 # Purpose: Sync .openclaw configuration and workspace to a Git-managed directory and push readable history. set -euo pipefail join_human() { local items=("$@") local count=${#items[@]} if [ "$count" -eq 0 ]; then printf '' elif [ "$count" -eq 1 ]; then printf '%s' "${items[0]}" elif [ "$count" -eq 2 ]; then printf '%s and %s' "${items[0]}" "${items[1]}" else local last="${items[$((count-1))]}" unset 'items[$((count-1))]' printf '%s, and %s' "$(IFS=', '; echo "${items[*]}")" "$last" fi } summarize_status() { local status_lines="$1" local lowered summary count local -a categories=() local -a details=() local -a skill_paths=() local skill_names='' skill_summary='' lowered=$(printf '%s\n' "$status_lines" | tr '[:upper:]' '[:lower:]') while IFS= read -r path; do [ -n "$path" ] && skill_paths+=("$path") done < <(printf '%s\n' "$lowered" | sed -n 's#.*workspace/skills/\([^/]*\)/.*#\1#p' | awk '!seen[$0]++') if [ ${#skill_paths[@]} -gt 0 ]; then skill_names=$(join_human "${skill_paths[@]}") if printf '%s\n' "$lowered" | grep -q 'workspace/skills/.*/scripts/'; then skill_summary="skills: update ${skill_names} scripts" else skill_summary="skills: update ${skill_names}" fi fi if printf '%s\n' "$lowered" | grep -qE '(^|[[:space:]])(openclaw\.json|identity/|devices/|cron/|extensions/|exec-approvals\.json|update-check\.json)'; then categories+=("config") fi if printf '%s\n' "$lowered" | grep -qE '(^|[[:space:]])(workspace/)'; then categories+=("workspace") if printf '%s\n' "$lowered" | grep -q 'workspace/archive/'; then details+=("archive") fi fi if printf '%s\n' "$lowered" | grep -qE '(^|[[:space:]])(memory/|memory\.sqlite)'; then categories+=("memory") fi if printf '%s\n' "$lowered" | grep -qE '(^|[[:space:]])(agents/|telegram/|delivery-queue/|canvas/|completions/)'; then categories+=("runtime") fi mapfile -t categories < <(printf '%s\n' "${categories[@]:-}" | awk 'NF && !seen[$0]++') mapfile -t details < <(printf '%s\n' "${details[@]:-}" | awk 'NF && !seen[$0]++') if [ -n "$skill_summary" ]; then printf '%s\n' "$skill_summary" return fi count=${#categories[@]} if [ "$count" -eq 0 ]; then summary="backup: update openclaw state" elif [ "$count" -eq 1 ]; then case "${categories[0]}" in workspace) if [ ${#details[@]} -gt 0 ]; then summary="workspace: update $(join_human "${details[@]}")" else summary="workspace: update workspace files" fi ;; config) summary="config: update runtime configuration" ;; memory) summary="memory: refresh memory data" ;; runtime) summary="runtime: update sessions and channel state" ;; *) summary="backup: update openclaw state" ;; esac else summary="backup: update $(join_human "${categories[@]}")" fi printf '%s\n' "$summary" } build_commit_body() { local status_lines="$1" local max_lines=12 local count=0 local extra=0 local body='' local line code path rest old new action display while IFS= read -r line; do [ -z "$line" ] && continue code=$(printf '%s' "$line" | cut -c1-2) rest=$(printf '%s' "$line" | cut -c4-) action='updated' display="$rest" case "$code" in '??') action='added' ;; 'A '|' A'|'AM'|'AA') action='added' ;; 'M '|' M'|'MM'|'RM'|'MR') action='modified' ;; 'D '|' D'|'MD'|'DM') action='deleted' ;; 'R '|' R'|'RR') old=${rest%% -> *} new=${rest#* -> } action='renamed' if printf '%s' "$new" | grep -q '^workspace/archive/'; then action='archived' display="$old to $new" else display="$old to $new" fi ;; 'C '|' C') old=${rest%% -> *} new=${rest#* -> } action='copied' display="$old to $new" ;; *) action='updated' ;; esac count=$((count+1)) if [ "$count" -le "$max_lines" ]; then body+="- ${action} ${display}"$'\n' else extra=$((extra+1)) fi done <<< "$status_lines" if [ "$extra" -gt 0 ]; then body+="- ... and ${extra} more changes"$'\n' fi body+=$'\n' body+="Backup run at $(date -u +'%Y-%m-%d %H:%M:%S UTC')" printf '%s' "$body" } REPO_URL=$(openclaw config get skills.entries.openclaw-backup-restore.env.OPENCLAW_BACKUP_REPO 2>/dev/null | tr -d '"') if [ -z "$REPO_URL" ] || [ "$REPO_URL" = "null" ]; then echo "Error: OPENCLAW_BACKUP_REPO is not set in openclaw.json." echo 'Please set it via: openclaw config set skills.entries.openclaw-backup-restore.env.OPENCLAW_BACKUP_REPO "your-git-repo-url"' exit 1 fi SOURCE="${HOME}/.openclaw/" BACKUP_DIR="${HOME}/openclaw-backup/" LOG_FILE="/tmp/openclaw-backup.log" echo "[$(date)] Starting OpenClaw backup to ${REPO_URL}..." | tee -a "$LOG_FILE" if [ ! -d "$BACKUP_DIR" ]; then echo "Initializing backup directory at $BACKUP_DIR..." mkdir -p "$BACKUP_DIR" cd "$BACKUP_DIR" git init git remote add origin "$REPO_URL" else cd "$BACKUP_DIR" git remote set-url origin "$REPO_URL" || true fi SKILL_DIR_SHARED="${HOME}/.openclaw/skills/openclaw-backup-restore" SKILL_DIR_WORKSPACE="${HOME}/.openclaw/workspace/skills/openclaw-backup-restore" SKILL_DIR="" if [ -f "${SKILL_DIR_SHARED}/.gitignore" ]; then SKILL_DIR="$SKILL_DIR_SHARED" elif [ -f "${SKILL_DIR_WORKSPACE}/.gitignore" ]; then SKILL_DIR="$SKILL_DIR_WORKSPACE" fi if [ ! -f "${BACKUP_DIR}/.gitignore" ] && [ -n "$SKILL_DIR" ] && [ -f "${SKILL_DIR}/.gitignore" ]; then echo "Copying .gitignore from skill directory..." cp "${SKILL_DIR}/.gitignore" "${BACKUP_DIR}/.gitignore" fi rsync -av --delete \ --exclude-from="${BACKUP_DIR}/.gitignore" \ --exclude=".git/" \ --exclude=".gitignore" \ "$SOURCE" "$BACKUP_DIR" cd "$BACKUP_DIR" STATUS=$(git status --short) if [ -n "$STATUS" ]; then COMMIT_MSG=$(summarize_status "$STATUS") COMMIT_BODY=$(build_commit_body "$STATUS") git add . git commit -m "$COMMIT_MSG" -m "$COMMIT_BODY" git push origin main echo "[$(date)] Changes committed and pushed successfully: $COMMIT_MSG" | tee -a "$LOG_FILE" else git push origin main || true echo "[$(date)] No changes detected, ensuring remote is synced." | tee -a "$LOG_FILE" fi echo "[$(date)] Backup complete! ☺️🌸" | tee -a "$LOG_FILE" ``` ### scripts/restore.sh ```bash #!/usr/bin/env bash # OpenClaw Restore Script ☺️🌸 # Purpose: Clones/pulls the backup from Git and restores it to the .openclaw directory. set -e # Fetch repository URL from OpenClaw config REPO_URL=$(openclaw config get skills.entries.openclaw-backup-restore.env.OPENCLAW_BACKUP_REPO 2>/dev/null | tr -d '"') if [ -z "$REPO_URL" ] || [ "$REPO_URL" == "null" ]; then echo "Error: OPENCLAW_BACKUP_REPO is not set." exit 1 fi BACKUP_DIR="${HOME}/openclaw-backup/" DEST_DIR="${HOME}/.openclaw/" echo "[$(date)] Starting OpenClaw restore process..." # 1. Ensure backup directory exists and is up to date if [ ! -d "$BACKUP_DIR" ]; then echo "Cloning backup repository..." git clone "$REPO_URL" "$BACKUP_DIR" else echo "Updating backup repository..." cd "$BACKUP_DIR" && git pull origin main fi # 2. Sync back to the production directory # Does not delete new files in production unless explicitly managed echo "Syncing files to $DEST_DIR..." mkdir -p "$DEST_DIR" rsync -av --exclude=".git/" --exclude=".gitignore" "$BACKUP_DIR" "$DEST_DIR" # 3. Restore dependencies echo "Restoring node dependencies..." cd "$DEST_DIR" # Check for package.json in typical plugin/extension locations find . -name "package.json" -not -path "*/node_modules/*" -execdir npm install \; # 4. Fix environment using OpenClaw doctor echo "Running openclaw doctor to fix environment..." openclaw doctor --yes echo "[$(date)] Restore complete! Please restart the gateway. ☺️🌸" ```