The backup command tree snapshots ~/.claude/ to a directory tree
under ~/.stackunderflow/backups/. It runs rsync with hard-linked
incrementals: after the first backup, unchanged files cost no extra
disk. Nothing is uploaded anywhere.
~/.claude/ is the source of truth for every Claude Code session
StackUnderflow ingests. It is also a moving target — Claude Code
rewrites the session JSONL files as sessions evolve, replaces plan and
task files in place, and occasionally rotates history. A misconfigured
hook, a bad merge, or a rm mishap can wipe weeks of work.
The local store at ~/.stackunderflow/store.db is derived data: it is
rebuilt from the source files on the next ingest pass. Backing up the
source files is the only way to make the dashboard restorable without
re-running every coding session.
stackunderflow backup create [--label TEXT] [--keep N]
stackunderflow backup list
stackunderflow backup restore NAME [--dry-run]
stackunderflow backup auto [--enable | --disable]
| Option | Default | Description |
|---|---|---|
--label TEXT |
none | Suffix appended to the timestamped directory name. Sanitised to [A-Za-z0-9_-]+ — anything else is stripped. |
--keep N |
10 |
Maximum number of backups to retain. Oldest are pruned after a successful run. Range: >= 1. |
The destination layout is:
~/.stackunderflow/backups/
20260514-123045/ # naked timestamp
20260514-123045-before-upgrade/ # with --label before-upgrade
20260514-123045-auto/ # from `backup auto`
...
The directory name is YYYYMMDD-HHMMSS[-label]. Sort order is
chronological because the prefix is fixed-width.
rsync -a copies everything under ~/.claude/ except these large or
rebuildable sub-trees:
debug/(diagnostic logs — ~GB)plugins/(re-installable binaries)cache/,statsig/,telemetry/,paste-cache/,ccnotify/,session-env/,downloads/backups/(Claude Code's own internal config backups — separate artefact)
Everything else lands in the snapshot: projects/*.jsonl,
history.jsonl, plans/, tasks/, teams/, todos/, skills/,
agents/, commands/, settings.json, shell-snapshots/,
statsig-stable-id.txt, prompt history, and so on.
backup create runs
rsync -a [--exclude PATTERN ...] [--link-dest PREVIOUS] SRC/ DEST/
where PREVIOUS is the most recent backup directory. With
--link-dest, rsync hard-links any file that is byte-identical to the
one in PREVIOUS rather than copying it. The result on disk is:
- A new directory tree that looks like a full copy of
~/.claude/. dfcharges you only for the deltas — files that were added, modified, or renamed since the last backup.du -sh ~/.stackunderflow/backups/<name>reports the apparent size, not the unique size. To see actual disk used, rundu -sh --total ~/.stackunderflow/backups/.
Restore semantics see hard-linked and copied files identically: a file
hard-linked from backups/20260514-090000/projects/foo.jsonl is fully
usable from backups/20260514-150000/projects/foo.jsonl. The two
inode entries point at the same blocks.
If rsync is not on $PATH (some minimal Linux containers, fresh
Windows installs), backup create falls back to shutil.copytree
with the same exclude list. The fallback does not deduplicate: each
backup is a full copy, and the command prints a one-line notice.
rsyncnon-zero exit → the partial destination is removed and the error is printed. The store is untouched.- Timeout > 10 minutes → the partial destination is removed and the command exits with a message.
- Invalid label (escapes the backup directory) → command exits without writing anything.
Lists every existing backup under ~/.stackunderflow/backups/. Per
backup it shows the JSONL file count and the total apparent size
in MB. (The "size" is apparent, not unique-on-disk — see the
hard-link contract above.)
Restores a named backup back over ~/.claude/. NAME is the
directory name from backup list (e.g. 20260514-150000-auto).
| Option | Description |
|---|---|
--dry-run |
Show how many files would be restored. Touches nothing. |
Without --dry-run, the command prompts before writing —
This will overwrite files in <home>/.claude. Continue? — and aborts
on anything but yes. The restore runs rsync -a SRC/ DEST/ (or
shutil.copytree if rsync is missing, same as backup create).
Existing files in ~/.claude/ that the backup does not contain are
left alone: rsync runs without --delete, so the operation merges
the backup into ~/.claude/ rather than wiping and replacing it.
Restore is idempotent — running it twice produces the same result the
second time. One caveat: if ~/.claude/ has diverged a lot from the
backup, wipe ~/.claude/projects/ by hand before restoring, otherwise
stale session files linger alongside the restored ones.
Enables a daily backup at 03:00 local time, keeping the last 10 snapshots.
On macOS: writes a launchd plist at
~/Library/LaunchAgents/com.stackunderflow.backup.plist and loads it
via launchctl load. The plist invokes stackunderflow backup create --label auto --keep 10. Standard output and error are written to
~/.stackunderflow/backup.log.
--disable unloads and removes the plist. If no plist is present, the
command says so and exits.
On Linux / other: prints a crontab -e line to paste in; --disable
prints the same line to remove. It never edits the crontab for you —
a crontab is user-managed, and a hand-written schedule should not be
clobbered.
- Before any
~/.claude/migration. Claude Code upgrades that touch the on-disk format. Runstackunderflow backup create --label before-upgradefirst. - Before a
--freshstart.stackunderflow start --freshclears~/.stackunderflow/cache/. That's safe, but if you also intend to re-ingest from scratch, a backup is a cheap insurance policy. - Before a destructive shell experiment. If you're about to mass-
delete files or run scripts that touch
~/.claude/, snapshot first. - Daily, by default, via
backup auto. Cheap, hard-linked, bounded by--keep.
Backups live on disk in ~/.stackunderflow/backups/. They are not
encrypted at rest, and they are not synced anywhere — nothing leaves
the machine.
If you need off-machine backups, copy the backup directory to your
preferred encrypted storage — the snapshot is a plain directory tree
of files, so tar, rsync, restic, or borg all work.