A config-driven system for backing up and restoring your complete Claude Code environment -- settings, memory, skills, plugins, plans, commands, sessions, and more.
- Overview
- What to Backup
- What NOT to Backup
- Quick Start
- Detailed Instructions
- Scheduling Automatic Backups
- Sanitized Export for Sharing
- Security
- Troubleshooting
- FAQ
Claude Code stores a rich set of data across your machine: global instructions, per-project memory, installed skills, plugins, saved plans, custom commands, session transcripts, and MCP server configurations. Losing any of these means rebuilding your environment from scratch.
This guide provides three scripts that back up the full Claude Code data model to a private Git repository:
init.sh-- First-time setup. Scans your projects, generatesbackup-config.json.backup.sh-- Config-driven, modular backup with auto-commit and optional auto-push. Fully non-interactive (safe for cron).restore.sh-- Interactive restore with per-category prompts (or--yesfor unattended restore).
Requirements: bash, git, jq
The v2 system backs up every category of Claude Code data that matters for portability:
| Category | Source Location | Backup Directory | Description |
|---|---|---|---|
| Global instructions | ~/.claude/CLAUDE.md |
global/ |
Custom instructions for all sessions |
| Extra context files | ~/.claude/*.md |
global/ |
Additional markdown context files |
| Settings | ~/.claude/settings.json |
global/ |
Basic settings (e.g., alwaysThinkingEnabled) |
| Local settings | ~/.claude/settings.local.json |
global/ |
Permission rules and advanced settings |
| Keybindings | ~/.claude/keybindings.json |
global/ |
Custom key bindings |
| MCP config | ~/.claude.json |
global/claude.json |
MCP server definitions, OAuth tokens, app state |
| Skills | ~/.claude/skills/ |
skills/ |
User-installed skill packages |
| Plugins | ~/.claude/plugins/ |
plugins/ |
Plugin registry (installed_plugins.json, blocklist.json, known_marketplaces.json) |
| Plans | ~/.claude/plans/ |
plans/ |
Saved implementation plans |
| Custom commands | ~/.claude/commands/ |
commands/ |
User-defined slash commands |
| Todos | ~/.claude/todos/ |
todos/ |
Per-session task state |
| Project memory | ~/.claude/projects/<name>/memory/ |
projects/<name>/memory/ |
Auto memory files (MEMORY.md, topic files) |
| Session transcripts | ~/.claude/projects/<name>/*.jsonl |
projects/<name>/sessions/ |
Session data for /resume support |
These are excluded because they are sensitive, machine-specific, or regenerated automatically:
| File / Directory | Reason to Exclude |
|---|---|
.credentials.json |
Authentication tokens (regenerated on login) |
history.jsonl |
Chat history -- potentially huge and sensitive |
file-history/ |
File operation history |
debug/ |
Debug logs and temporary data |
statsig/, Cache/ |
Analytics and cache data |
plugins/cache/, plugins/marketplaces/ |
Re-downloaded on next launch |
projects/* (non-memory, non-session files) |
Runtime state, not portable |
git clone https://github.com/jtklinger/claude-code-backup-guide.git
cd claude-code-backup-guide# Create a new directory for your backups (separate from this guide repo)
mkdir ~/claude-code-backup
cd ~/claude-code-backupThe init script scans your ~/.claude/projects/ directory, lets you select which projects to include, and generates backup-config.json:
bash /path/to/claude-code-backup-guide/scripts/init.sh ~/claude-code-backupYou will see a numbered list of discovered projects. Type all, none, or specific numbers separated by spaces.
bash /path/to/claude-code-backup-guide/scripts/backup.sh ~/claude-code-backupThe script copies all configured data categories, stages changes, and commits automatically.
cd ~/claude-code-backup
git remote add origin git@github.com:YOUR_USER/claude-code-backup.git
git push -u origin maingit clone git@github.com:YOUR_USER/claude-code-backup.git
cd claude-code-backup
# Interactive restore (prompts for each category)
bash /path/to/claude-code-backup-guide/scripts/restore.sh .
# Or non-interactive (restore everything)
bash /path/to/claude-code-backup-guide/scripts/restore.sh . --yesThe config file lives in the root of your backup repository. It controls what gets backed up:
{
"version": 1,
"claude_dir": "~/.claude",
"include_sessions": true,
"include_todos": true,
"projects": [
"C--Users-jtkli-projects-Homelab",
"C--Users-jtkli-projects-my-app"
],
"git_auto_push": false,
"git_remote": "origin",
"git_branch": "main"
}| Field | Type | Description |
|---|---|---|
version |
number | Config format version (must be 1) |
claude_dir |
string | Path to Claude Code data directory (~ is expanded) |
include_sessions |
boolean | Whether to back up .jsonl session transcripts per project |
include_todos |
boolean | Whether to back up per-session todo state |
projects |
string[] | List of project directory names from ~/.claude/projects/ |
git_auto_push |
boolean | Push to remote after each backup commit |
git_remote |
string | Git remote name for auto-push |
git_branch |
string | Git branch name for auto-push |
Project directory names use Claude Code's encoding scheme where path separators become dashes (e.g., C--Users-jtkli-projects-Homelab represents C:/Users/jtkli/projects/Homelab). The init.sh script handles this mapping for you.
After running a backup, your repository will look like this:
claude-code-backup/
├── backup-config.json
├── .gitignore
├── global/
│ ├── CLAUDE.md
│ ├── settings.json
│ ├── settings.local.json
│ ├── keybindings.json
│ ├── claude.json # ~/.claude.json (MCP config + OAuth)
│ └── HOMELAB-CONTEXT.md # any extra *.md files
├── skills/
│ └── <skill-packages>/
├── plugins/
│ ├── installed_plugins.json
│ ├── blocklist.json
│ └── known_marketplaces.json
├── plans/
│ └── <plan>.md
├── commands/
│ └── <command>.md
├── todos/
│ └── <session-id>.json
└── projects/
└── C--Users-jtkli-projects-Homelab/
├── memory/
│ ├── MEMORY.md
│ └── <topic>.md
└── sessions/
├── <session-id>.jsonl
└── <session-id>.meta.json
Usage: bash init.sh [backup-directory]
- Creates the backup directory if it does not exist
- Initializes a Git repository
- Copies the
.gitignoretemplate - Scans
~/.claude/projects/and presents an interactive project selector - Generates
backup-config.json - Only needs to be run once (re-run to add new projects)
Usage: bash backup.sh [backup-directory] [--sanitize <output-directory>]
- Reads
backup-config.jsonfrom the backup directory - Copies all data categories (global settings, MCP config, skills, plugins, plans, commands, todos, project data)
- Only copies files that have changed (uses
cmpfor diffing) - Removes stale files from the backup that no longer exist in the source
- Stages, commits, and optionally pushes
- Fully non-interactive -- safe for cron or Task Scheduler
- If no backup-directory argument is provided, assumes the script's parent directory is the backup repo
--sanitize <output-directory>produces a credential-free export in the given directory (see Sanitized Export)
Usage: bash restore.sh [backup-directory] [--yes]
- Reads
backup-config.jsonfrom the backup directory - Prompts interactively for each category (global settings, MCP config, skills, plugins, plans, commands, todos, projects)
- Creates timestamped backups of existing files before overwriting
--yesflag skips all prompts and restores everything- Session files are restored from
sessions/subdirectory back to the project root (where Claude Code expects them) - Prints a verification summary with file counts and sizes
Since backup.sh is fully non-interactive and auto-commits, it works well as a scheduled task.
# Run backup daily at 2:00 AM
crontab -e
# Add this line:
0 2 * * * /path/to/scripts/backup.sh /path/to/claude-code-backup >> /tmp/claude-backup.log 2>&1- Open Task Scheduler
- Create a new task:
- Trigger: Daily (or at logon, or whatever frequency you prefer)
- Action: Start a program
- Program:
C:\Program Files\Git\bin\bash.exe - Arguments:
/path/to/scripts/backup.sh /path/to/claude-code-backup
To push to your remote after every backup, edit backup-config.json:
{
"git_auto_push": true,
"git_remote": "origin",
"git_branch": "main"
}The backup script will attempt to push after committing. If the push fails, it logs a warning but does not exit with an error.
The --sanitize flag produces a credential-free copy of your settings, safe for sharing publicly or with teammates.
# Run backup and export sanitized copy
bash scripts/backup.sh ~/claude-code-backup --sanitize ~/claude-export
# Share the export directory
cd ~/claude-export
git init && git add -A && git commit -m "Claude Code settings template"
git remote add origin git@github.com:YOUR_USER/claude-code-template.git
git push -u origin main| Data | Action |
|---|---|
| MCP server hostnames | Replaced with <HOSTNAME> |
| MCP usernames | Replaced with <USERNAME> |
| SSH key paths | Replaced with <SSH_KEY_PATH> |
| Auth tokens / Bearer tokens | Replaced with <AUTH_TOKEN> |
| URLs in MCP args | Replaced with <URL> |
| Environment variable values | Replaced with <REDACTED> |
| File paths in MCP args | Replaced with <PATH> |
| MCP permission names | Server names replaced with <server> |
| OAuth account data | Removed entirely |
| App state (counters, caches) | Removed entirely |
| Sessions and todos | Excluded from export |
- CLAUDE.md and extra context files (as-is)
- Settings structure (plugins, keybindings, preferences)
- MCP server names and types (structure without credentials)
- Skills, plans, commands, plugin registry (as-is)
- Per-project memory (interactive selection)
- Copy the export files into
~/.claude/ - Edit
global/claude.json— replace<PLACEHOLDER>values with your server details - Edit
global/settings.json— updatemcp__<server>__*permission entries - Restart Claude Code
The backup includes ~/.claude.json, which contains:
- MCP server configurations with connection details
- OAuth tokens for authenticated MCP servers
- App state and other runtime data
It also includes settings.local.json, which may contain:
- Approved command patterns that reveal your infrastructure
- File paths and directory structures
Never use a public repository for your backup.
Unlike v1, v2 backs up ~/.claude.json and settings.local.json as-is (no sanitization). This is intentional -- sanitized files lose MCP server definitions and cannot be restored without manual reconfiguration.
The trade-off is clear: your backup repo is sensitive and must be private. Treat it like you would a .env file or SSH private key.
If you accidentally push to a public repo or your backup repo is compromised:
- Immediately rotate all OAuth tokens and passwords in your MCP server configurations
- Re-authenticate with Claude Code
- Remove the repository from public access
- Consider using
git filter-branchor BFG Repo-Cleaner to purge history
Use SSH keys (not HTTPS with tokens) for your backup repository remote. This avoids storing additional credentials in your Git config:
git remote add origin git@github.com:YOUR_USER/claude-code-backup.gitProblem: backup.sh or restore.sh exits with "jq is required but not installed"
Solution: Install jq for your platform:
# macOS
brew install jq
# Debian/Ubuntu
sudo apt install jq
# RHEL/Rocky/Fedora
sudo dnf install jq
# Windows (winget)
winget install jqlang.jq
# Windows (scoop)
scoop install jqProblem: Backup repository grows quickly due to .jsonl session transcripts
Solution: Disable session backup if you do not need /resume support across machines:
{
"include_sessions": false
}Or periodically clean old sessions from the backup:
# Remove session files older than 30 days from the backup
find ~/claude-code-backup/projects/*/sessions/ -name "*.jsonl" -mtime +30 -deleteProblem: Per-project memory (MEMORY.md, topic files) not appearing after restore
Solutions:
- Verify the project is listed in
backup-config.jsonunderprojects - Check that the project directory name matches exactly (case-sensitive)
- Restart Claude Code after restoring -- memory files are read on startup
- Verify the files exist:
ls ~/.claude/projects/<project-name>/memory/
Problem: Settings do not take effect after running restore.sh
Solution:
- Restart Claude Code completely (quit and relaunch)
- Check file permissions:
ls -la ~/.claude/ - Verify file contents:
cat ~/.claude/settings.json - Check for
.backup.*files that may indicate the restore created backups of conflicting files
Problem: MCP servers show as disconnected after restoring ~/.claude.json
Solutions:
- OAuth tokens in
~/.claude.jsonmay have expired -- re-authenticate with the affected services - Verify network connectivity to the MCP server endpoints
- Check that any local dependencies (npm packages, binaries) are installed on the new machine
- Run
claude mcp listto see the current status
Problem: backup.sh says "No changes detected" even though you changed settings
Solution: The script compares files byte-for-byte using cmp. If the files are identical, no commit is created. Verify the source files actually changed:
diff ~/.claude/settings.json ~/claude-code-backup/global/settings.jsonA: v1 backed up only CLAUDE.md, settings.json, settings.local.json, and a text dump of MCP servers. v2 is a config-driven system that backs up the complete Claude Code data model: memory, skills, plugins, plans, commands, keybindings, the full ~/.claude.json, session transcripts, and todos. It uses three purpose-built scripts (init, backup, restore) instead of manual copy commands.
A: No. Chat history (history.jsonl) is excluded intentionally -- it can be hundreds of megabytes and contains the full text of every conversation. If you need to preserve specific conversations, export them individually. Session transcripts (.jsonl in project directories) are a different thing and are backed up for /resume support.
A: No. v2 backs up ~/.claude.json which contains OAuth tokens and MCP server credentials. Your backup repository must be private. If you want to share your CLAUDE.md or settings as a template, copy those specific files into a separate public repository.
A: Set up a cron job or Task Scheduler task to run backup.sh daily. The script is idempotent -- if nothing changed, no commit is created. For critical changes (new MCP servers, major CLAUDE.md updates), run a manual backup immediately.
A: Partially. You can share CLAUDE.md, plans, commands, and skills through a shared private repository. However, ~/.claude.json and settings.local.json contain per-user credentials and should not be shared. Consider maintaining a team template repo alongside individual backup repos.
A: Project-specific CLAUDE.md files live in the project's own repository root and should be committed there, not in your settings backup. This guide backs up per-project memory (auto-generated context) and sessions (for /resume), which live under ~/.claude/projects/.
A: The settings files themselves are platform-agnostic. However, project directory names encode the full path (e.g., C--Users-jtkli-projects-Homelab), so a backup from Windows will have Windows-style encoded paths. If restoring on macOS/Linux, you may need to update the projects list in backup-config.json to match the new machine's paths.
A: Yes. Either re-run init.sh (it will ask before overwriting the config), or manually edit backup-config.json and add the project directory name to the projects array. Find directory names with ls ~/.claude/projects/.
A: The restore script creates timestamped backups of any existing file before overwriting it (e.g., settings.json.backup.20260305_140000). You can always roll back by copying the .backup.* file over the restored version.
Found an issue or have an improvement? Please:
- Fork this repository
- Create a feature branch
- Submit a pull request
MIT License - Feel free to use and modify as needed.