/community/claude backup
innernet
docsskillscommunitycompanydeveloperssecurityprivacytermscontact

a skill by finniganjohnson, brought here by SD

claude backup

paste this link into your ai. it will know what to do.

/skills/finniganjohnson-claude-backup

Sets up, runs and restores an unattended weekly backup of Claude Code memories, skills, CLAUDE.md files and settings into a private git repo. Use whenever the user wants to back up, mirror, snapshot, preserve, migrate or restore ~/.claude or their Claude Code memory, skills or config; worries about losing memories if a laptop dies or is replaced; wants the same Claude Code setup on a new machine; or asks to schedule or automate a backup of their Claude setup, even without saying "backup".

kindskill
last tended2026-09-21
written byfinniganjohnson
sourcegithub.com/finniganjohnson/fj-skills
kept currentwith its source · last read 2026-11-05
brought here bySD
reuseMIT
yours?it stays under your name. to change it or take it down, write to connect@innernet.live
reading8 min

Claude Backup

Overview

Claude Code keeps its most valuable state on one disk: per-project memories in ~/.claude/projects/<slug>/memory/, skills, CLAUDE.md files and settings. This skill gives the user a small bash script plus a scheduler job that mirror the durable parts of that state into a private git repo every week, and a documented way to put it all back on a new machine.

Three design rules make it safe to run unattended. Keep them intact:

  1. 1Allowlist, not denylist. Only the paths listed in references/scope.md are copied. Whatever Claude Code adds to ~/.claude next (caches, transcripts, token files) stays out by default.
  2. 2Exact mirror of four roots. dotclaude/, dotclaude-projects/, claude-mds/ and dotfiles/ are rebuilt from scratch each run and are the only paths the script ever stages. Nothing else in the repo can be swept into a backup commit. Deletions propagate; git history keeps the old file.
  3. 3Snapshot, not incremental. A missed week loses nothing; the next run captures the current state.

Bundled files, relative to this skill's directory (${CLAUDE_SKILL_DIR}; if that is empty, use the base directory shown when this skill loaded):

PathPurpose
scripts/claude-backupthe backup script, bash, set -euo pipefail
scripts/install-launchd, assets/local.claude-backup.plistmacOS scheduler
scripts/install-systemd, assets/claude-backup.service, assets/claude-backup.timerLinux scheduler
scripts/smoke-testend-to-end test against a fake HOME and a scratch repo
assets/REPO-README.mdREADME template for the user's backup repo
references/scope.mdwhat is mirrored, what is not, and why
references/restore.mdrestoring on a new machine, troubleshooting

Which task?

The user wants to...Section
set this up for the first timeSetup
run a backup right nowRun now
know whether backups are workingCheck
get their config onto a new machineRestore
change when it runs, or turn it offSchedule

Setup

1. Confirm the inputs

Five facts decide everything. Ask only for the ones the conversation has not already supplied, and propose the default for each.

InputDefaultWhy it matters
Backup repo: path, private, remote origin, default branch mainthe current repoThe script refuses to run off main and pushes to origin. Private is non-negotiable: settings files can carry API keys in env blocks, and memories describe internal projects.
Projects directory~/projectsEvery <projects>/<name>/.claude/ and CLAUDE.md is mirrored.
Shell rc file~/.zshrcMirrored into dotfiles/. Often exports tokens; step 5 checks.
ScheduleSaturday 12:00 localWeekly suits a snapshot. Pick a time the machine is usually awake.
OSdetect with unamelaunchd on macOS, systemd user timer on Linux.

Check visibility rather than assuming: when the remote is on GitHub and gh is installed, gh repo view --json visibility -q .visibility must print PRIVATE; otherwise ask the user to confirm. If the repo is public, stop and say so. Do not continue with a public repo.

2. Copy the bundled files into the repo

Copy the scripts into the backup repo instead of running them from the skill directory. The script finds its repo with git rev-parse --show-toplevel on its own location, and a repo that carries its own tooling can be restored on a machine that does not have this skill installed. From the repo root:

mkdir -p claude-backup/bin claude-backup/launchd claude-backup/systemd
cp "${CLAUDE_SKILL_DIR}"/scripts/claude-backup "${CLAUDE_SKILL_DIR}"/scripts/install-launchd \
   "${CLAUDE_SKILL_DIR}"/scripts/install-systemd "${CLAUDE_SKILL_DIR}"/scripts/smoke-test claude-backup/bin/
cp "${CLAUDE_SKILL_DIR}"/assets/local.claude-backup.plist claude-backup/launchd/
cp "${CLAUDE_SKILL_DIR}"/assets/claude-backup.service "${CLAUDE_SKILL_DIR}"/assets/claude-backup.timer claude-backup/systemd/
chmod +x claude-backup/bin/*
grep -qx '.DS_Store' .gitignore 2>/dev/null || echo '.DS_Store' >> .gitignore

3. Apply the inputs

  • Non-default projects dir, shell rc or Claude dir: set PROJECTS_DIR,

SHELL_RC (and, rarely, CLAUDE_DIR) as environment variables in the scheduler definition rather than editing the script, so the script stays byte-identical to the tested one. launchd: add keys to the plist's EnvironmentVariables dict. systemd: add Environment=PROJECTS_DIR=/path lines to the service unit. For manual runs, export them in the shell or note them in the README. The script refuses to run, naming the variable, when PROJECTS_DIR or SHELL_RC does not resolve, so a manual run that forgets an export fails loudly instead of committing an empty mirror root.

  • Schedule: edit StartCalendarInterval in the plist (Weekday 0 is

Sunday, 6 is Saturday) or OnCalendar= in the timer.

4. Prove it works before touching the real repo

claude-backup/bin/smoke-test

The test builds a fake HOME and a scratch repo with a bare origin, runs the backup six times and checks 33 things: what is mirrored, what is excluded, that the log reports per-root counts, that deletions propagate, that an unrelated file is never committed, that a no-op run makes no commit, that the wrong branch is refused, and that a missing projects dir or shell rc is refused rather than emptying a mirror root. Expect 33 passed, 0 failed. It works entirely inside a temporary directory under $TMPDIR and never touches the real ~/.claude or the real repo. Fix any failure before continuing: an unattended job that is wrong on day one is wrong every week.

5. Look for secrets before the first commit

The mirror is an allowlist, but a few allowed files commonly carry secrets: settings.json and settings.local.json (env blocks, MCP server configs) and the shell rc (export SOME_TOKEN=). Check the live sources:

grep -n -i -E 'sk-[a-z0-9]|ghp_|AKIA|token|secret|password|api[_-]?key' \
  ~/.claude/settings.json ~/.claude/settings.local.json <shell rc> \
  <projects dir>/*/.claude/settings.local.json 2>/dev/null

Substitute the shell rc and projects dir confirmed in step 1; with the defaults that is ~/.zshrc and ~/projects.

Show every hit and let the user decide: move the secret into a separate, unmirrored file that the rc sources, or accept it because the repo is private. Do not continue past a hit without the user's answer.

6. Commit the tooling and write the README

Render assets/REPO-README.md into the repo's README.md, filling in {{SCHEDULE}} (for example "Runs every Saturday at 12:00 local time via launchd"), {{PROJECTS_DIR}}, {{SHELL_RC}}, {{SHELL_RC_NAME}} (the rc file name without its leading dot, for example zshrc) and {{REPO_PATH}} (where the user keeps this clone). Then commit by hand, because the backup script only ever stages the four mirror roots:

git add claude-backup README.md .gitignore
git commit -m "Add claude-backup: weekly mirror of Claude Code config"
git push

7. First run, by hand

claude-backup/bin/claude-backup
git log -1 --stat
git status -sb

The script prints timestamped log lines, including how many memory files, project .claude/ directories and project CLAUDE.md files were mirrored. Expect a commit titled backup: <date> — N file(s) changed and main...origin/main in sync.

8. Install the scheduler

macOS:

claude-backup/bin/install-launchd
launchctl print "gui/$(id -u)/local.claude-backup" | grep -E 'state|last exit code'

Linux:

claude-backup/bin/install-systemd
systemctl --user list-timers claude-backup.timer
loginctl enable-linger "$USER"

Prefer these over cron. launchd's StartCalendarInterval and systemd's Persistent=true both fire a missed run when the machine wakes; cron skips it silently. The Linux installer was authored on macOS and syntax-checked only, so confirm the timer is listed.

9. Report

Tell the user what was mirrored (the script's count), the commit on origin, when the next run is, where the log lives, and the one-line command to back up by hand.

Run now

From anywhere inside the repo: claude-backup/bin/claude-backup. Safe at any time; a run with nothing new logs "nothing to back up" and exits 0. Through the scheduler: launchctl kickstart "gui/$(id -u)/local.claude-backup" or systemctl --user start claude-backup.service.

Check

  • git -C <repo> log --oneline -8: expect backup: commits on schedule. A

quiet week makes no commit; that is the no-op path, not a failure.

  • Log: tail -n 30 ~/Library/Logs/claude-backup.log on macOS,

journalctl --user -u claude-backup -n 30 on Linux.

  • Job state: launchctl print "gui/$(id -u)/local.claude-backup" | grep -E 'state|last exit code'

or systemctl --user status claude-backup.timer.

  • Failures raise a desktop notification and a log line starting ERROR:.

Causes and fixes are in references/restore.md.

Restore

Follow references/restore.md. In short: clone the backup repo, rsync dotclaude/ into ~/.claude/ (this overwrites same-named files, so say so first), copy per-project .claude/ and CLAUDE.md only for projects that exist on the new machine, copy the shell rc, then re-run the installer so the new machine keeps backing up.

Schedule

Change the time in claude-backup/launchd/local.claude-backup.plist or claude-backup/systemd/claude-backup.timer, commit, and re-run the installer; it replaces the existing job. Turn it off with install-launchd --uninstall or install-systemd --uninstall. To widen what is backed up, read the "Extending" section of references/scope.md first.

Common mistakes

MistakeWhy it bitesInstead
Running the script from the skill directoryIt resolves its repo from its own path, so it refuses to run or writes into the wrong repoCopy it into the backup repo first (step 2)
"Just back up all of ~/.claude"Transcripts run to hundreds of MB and grow weekly; ~/.claude.json holds OAuth tokens; caches churn every sessionKeep the allowlist; extend it deliberately via references/scope.md
A public repo "because it's only config"settings.json env blocks and shell rcs carry API keys; memories name internal systemsPrivate repo, plus the grep in step 5
cron instead of launchd or systemdcron skips the run while the lid is closed and never says soThe bundled installers; both fire on wake
Hand-editing files under the four mirror rootsThe next run overwrites themEdit the live file in ~/.claude; the next run picks it up
Expecting the backup commit to include other filesOnly the four roots are staged, by designCommit your own files yourself
Running by hand from a plain shell when the scheduler sets PROJECTS_DIR or SHELL_RCThe defaults (~/projects, ~/.zshrc) may not exist on this machineThe script refuses and names the variable; export it for the run, or wrap the command in an alias that does
Skipping the smoke testA wrong scope or a wrong branch check goes unnoticed for weeksRun it; expect 33 passed

Quick reference

TaskCommand
Back up nowclaude-backup/bin/claude-backup
Testclaude-backup/bin/smoke-test
Install or remove schedule, macOSclaude-backup/bin/install-launchd [--uninstall]
Install or remove schedule, Linuxclaude-backup/bin/install-systemd [--uninstall]
Log~/Library/Logs/claude-backup.log or journalctl --user -u claude-backup
Env overridesCLAUDE_BACKUP_REPO CLAUDE_DIR PROJECTS_DIR SHELL_RC CLAUDE_BACKUP_NO_NOTIFY

keep it where your ai can reach it.

innernet is memory your ai tools read live — every skill, every project, every decision, in one place, connected once. save this skill to yours, or publish one of your own as a link like this.

save it to your innernetsee every skill