Skip to main content
  1. Posts/

Syncing Claude Code Across Multiple Machines

· David Steeman · AI, Self-Hosted, Linux

I use Claude Code daily on two machines — a desktop workstation in my home office and a ThinkPad laptop. It’s a fantastic CLI-based AI coding assistant, but it has a gap: no built-in way to keep your configuration, custom commands, and project context in sync across machines.

After one too many times of building a great custom skill on one machine and having to manually copy it to the other, I decided to fix this properly. Here’s how I set up seamless multi-machine sync using tools I already had: a Synology NAS, symlinks, and Syncthing.

The Problem
#

Claude Code stores everything — skills, custom commands, plugins, settings, API keys, and per-project memory — in ~/.claude/. When you work on two machines, you quickly run into:

  • Custom skills built on one machine that don’t exist on the other
  • API keys drifting out of sync between .bashrc files
  • Project-specific Claude memory lost when you switch machines
  • Settings changes applied on one machine and forgotten on the other

The Key Insight
#

Not everything in ~/.claude/ should be synced the same way. The directory contains fundamentally different types of data:

~/.claude/
├── settings.json          ← Should be shared
├── commands/              ← Should be shared
├── skills/                ← Should be shared
├── plugins/               ← Should be shared
├── projects/              ← Should be shared
├── .credentials.json      ← Must stay local
├── sessions/              ← Must stay local
├── cache/                 ← Must stay local
├── history.jsonl          ← Must stay local
└── ...

Settings and configuration need to be identical everywhere. Project files need fast local I/O. Session state must stay machine-local. This naturally splits into three sync strategies.

Layer 1: NAS + Symlinks for Settings#

The simplest approach for shared config: store the files on a network drive and symlink to them.

I have a Synology NAS with a dedicated SMB share called ClaudeCode, mounted at /mnt/claudecode-nas on both machines via /etc/fstab:

//192.168.1.222/ClaudeCode /mnt/claudecode-nas cifs credentials=/etc/smb-credentials-claudecode,uid=1000,gid=1000,iocharset=utf8,vers=3.0,_netdev 0 0

Then on each machine, ~/.claude/ contains symlinks pointing to the NAS:

~/.claude/settings.json          -> /mnt/claudecode-nas/settings.json
~/.claude/commands/              -> /mnt/claudecode-nas/commands/
~/.claude/skills/                -> /mnt/claudecode-nas/skills/
~/.claude/plugins/               -> /mnt/claudecode-nas/plugins/
~/.claude/projects/              -> /mnt/claudecode-nas/projects/

Everything else (sessions, caches, credentials, history) stays local.

When Claude Code reads ~/.claude/settings.json, it follows the symlink to the NAS. Any change is instantly visible on all machines. No sync daemon, no conflict resolution, no polling — it’s just a file on a share.

API Keys
#

API keys were previously hardcoded in ~/.bashrc on each machine. Now they live in a single shared file on the NAS:

# /mnt/claudecode-nas/claude-env.sh
export ANTHROPIC_API_KEY="sk-ant-..."
export ZAI_API_KEY="7a5d9979..."

Each machine symlinks ~/.claude-env.sh to the NAS copy, and .bashrc sources it. To rotate a key, edit one file — done.

Automated Setup
#

Setting this up on a new machine is a single command: /mnt/claudecode-nas/setup-sync.sh. The script symlinks directories and files, sets up API keys, backs up any existing local data, and is fully idempotent.

Layer 2: Syncthing for Project Files
#

You could store projects on the NAS too, but that has real downsides:

  • I/O performance: SMB adds latency. Compiling, testing, and git operations are noticeably slower
  • Offline access: If the NAS goes down, you can’t work
  • Node modules: Tens of thousands of tiny files that SMB handles poorly

Syncthing keeps a full local copy on each machine and syncs changes peer-to-peer. You get full local disk performance with automatic synchronization.

sudo apt install syncthing
systemctl --user enable --now syncthing

Configure it through the web UI at http://localhost:8384, share the ~/claudecode/projects/ folder between machines, and you’re done. Changes propagate within ~10 seconds.

Why Not Just Git?
#

Most of these projects are git repos with GitHub remotes. But Syncthing syncs everything git doesn’t: uncommitted changes, staged files, stashes, local configs, and non-tracked files. No need to commit half-done work just to continue on another machine. Git remains the source of truth for committed code; Syncthing just makes the working directory portable.

Layer 3: Split Changelogs
#

Claude Code writes session changelogs — summaries of what was done. I had all 140+ in one flat directory, but they’re really two different things:

  1. Machine-specific work: “Set up Samba on john-ai”, “Fixed Proxmox VM” — meaningless on other machines
  2. Project-specific work: “Added games to Tiltbox”, “Refactored cc-switch” — relevant everywhere

So I split them:

~/claudecode/changelogs/
├── john-ai/                    ← Machine-local, not synced
└── john-ThinkPad-E15/          ← Machine-local, not synced

~/claudecode/projects/
├── Tiltbox/
│   └── changelog.md            ← Project changelog, synced via Syncthing
├── cc-switch/
│   └── changelog.md            ← Project changelog, synced via Syncthing

The /changelog custom command was updated to route accordingly — project changelogs travel with the project, machine changelogs stay local.

The Complete Picture
#

┌─────────────────────────────────────────────────────┐
│                  john-ai (Desktop)                    │
│                                                       │
│  ~/.claude/                                           │
│  ├── settings.json ──┐                                │
│  ├── commands/ ───────┤  NAS Symlinks                 │
│  ├── skills/ ─────────┼─────► /mnt/claudecode-nas/    │
│  ├── plugins/ ────────┤       (Synology SMB share)    │
│  ├── projects/ ───────┘                                │
│  └── (sessions, cache, etc.)  ← Machine-local only    │
│                                                       │
│  ~/claudecode/projects/ ◄══ Syncthing ══► (laptop)    │
│  ~/claudecode/changelogs/john-ai/   ← NOT synced      │
└─────────────────────────────────────────────────────┘

The laptop has the same symlink structure pointing to the same NAS share, the same Syncthing folder, and its own machine-local changelogs.

Trade-offs
#

NAS dependency: If the NAS goes down, Claude Code falls back to defaults and you lose customization. Fine for an always-on home network, less ideal for a frequently disconnected laptop.

Symlink quirks: Some tools resolve symlinks when you cd into them. Git and Claude Code handle this fine.

Syncthing conflicts: Simultaneous edits on both machines create .sync-conflict files that need manual resolution. Rare in practice since you’re usually on one machine at a time.

Absolute paths: Some plugin config uses absolute paths like /home/john/.claude/plugins/cache/.... This works because both machines share the same username and home path. If they didn’t, plugins would need special handling.

Unencrypted secrets: API keys are in plaintext on the NAS. The share is password-protected and local-network-only, but for production use, consider a secrets manager.

Adding a Third Machine
#

  1. Mount the NAS share at /mnt/claudecode-nas
  2. Run /mnt/claudecode-nas/setup-sync.sh
  3. Install Syncthing, pair with existing devices, share the projects folder
  4. source ~/.bashrc

Done in about five minutes — most of that is waiting for Syncthing’s initial sync.

By the Numbers
#

MetricValue
Projects synced30 directories (~253,000 files)
Custom commands shared8 slash commands
API keys centralized2 (Anthropic, z.ai)
Setup time for new machine~5 minutes

The whole setup leans on simple, battle-tested tools — SMB mounts, symlinks, and peer-to-peer sync — rather than trying to build a custom synchronization layer. It works because Claude Code’s own directory structure naturally separates shared config from machine-local state. If you’re using Claude Code across multiple machines, give it a try.