Phase 7 — Migration
Move an existing OpenClaw deployment to a new host without losing config, memory, channel sessions, or scheduled tasks. This guide covers both same-OS and cross-platform migrations.
Prerequisites: You should have a working deployment on the source machine (any phase) and a target machine with the base OS ready. The target doesn’t need OpenClaw installed yet — this guide covers that.
Migration Overview
An OpenClaw deployment consists of these components:
| Component | Location | Transferable? | Notes |
|---|---|---|---|
| Config | ~/.openclaw/openclaw.json | Yes | Update paths for new host |
| Workspaces | ~/.openclaw/workspaces/<agent>/ | Yes | SOUL.md, memory files, research |
| WhatsApp credentials | ~/.openclaw/credentials/whatsapp/ | Yes | Baileys store (hundreds of files) |
| Signal credentials | ~/.openclaw/credentials/signal/ | No | Must re-pair on new host |
| Auth profiles | ~/.openclaw/agents/<id>/agent/auth-profiles.json | Yes | Copy to each agent |
| Identity files | ~/.openclaw/identity/ | Yes | Agent identity/keys |
| Session history | ~/.openclaw/agents/<id>/sessions/*.jsonl | Optional | Large; start fresh if possible |
| Google Chat credentials | ~/.openclaw/credentials/googlechat/ | Yes | Service account JSON — update path + webhook URL |
| Search workspace | ~/.openclaw/workspaces/search/ | Yes | Same as main agent workspaces |
| Search agentDir | ~/.openclaw/agents/search/ | Yes | Sessions, auth profiles |
| Extensions | ~/.openclaw/extensions/ | Yes | Plugin source + node_modules |
| Memory search index | Internal (architecture-dependent) | No | Rebuild on target: openclaw memory index |
| Secrets | Plist env vars / secrets.env | Manual | Re-enter on target (never copy plists with secrets over network) |
| Service files | LaunchAgent/systemd (or LaunchDaemon for hardened) | Recreate | Paths and users differ per host |
| Cron jobs / scheduled tasks | /etc/newsyslog.d/, /etc/logrotate.d/, crontab | Recreate | Log rotation, session pruning, temp cleanup |
Step 1 — Prepare the Source
Stop the gateway
# macOS (LaunchDaemon — default for dedicated headless accounts)
sudo launchctl bootout system/ai.openclaw.gateway
# macOS (LaunchAgent — if openclaw user has auto-login or active GUI session)
# sudo launchctl bootout gui/$(id -u openclaw)/ai.openclaw.gateway
# Linux (systemd)
sudo systemctl stop openclaw-gatewayCreate a backup
Option A: Built-in backup CLI (recommended — includes manifest and verification):
# Full backup (config + workspaces + credentials)
sudo -u openclaw bash -c 'cd /Users/openclaw && HOME=/Users/openclaw openclaw backup create'
# Config-only backup (no workspace/memory files)
sudo -u openclaw bash -c 'cd /Users/openclaw && HOME=/Users/openclaw openclaw backup create --only-config'
# Verify a backup archive before transferring
sudo -u openclaw bash -c 'HOME=/Users/openclaw openclaw backup verify openclaw-backup-*.tar.gz'Option B: Manual tar (works without the gateway installed):
# Full .openclaw directory backup
sudo tar czf openclaw-backup-$(date +%Y%m%d).tar.gz -C /Users/openclaw .openclaw
# Or for Linux
sudo tar czf openclaw-backup-$(date +%Y%m%d).tar.gz -C /home/openclaw .openclawSession files can be large. If you don’t need conversation history, exclude them with
--no-include-workspace(CLI) or--exclude='.openclaw/agents/*/sessions'(tar). Memory files (in workspaces) are separate and much smaller.
Export secrets inventory
List which secrets your deployment uses — don’t copy values over insecure channels:
# macOS: list env vars from plist (keys only, not values)
sudo -u openclaw /usr/libexec/PlistBuddy -c "Print :EnvironmentVariables" \
/Users/openclaw/Library/LaunchAgents/ai.openclaw.gateway.plist | grep "=" | awk '{print $1}'
# Linux: list env var keys from secrets file
sudo grep -oP '^\w+' /etc/openclaw/secrets.envCommon secrets to track:
ANTHROPIC_API_KEYOPENCLAW_GATEWAY_TOKEN(generate new on target —openssl rand -hex 32)BRAVE_API_KEYOPENROUTER_API_KEY(for content-guard and image-gen plugins)GITHUB_TOKEN(consider generating a new PAT scoped to the new host)GOOGLE_CHAT_SERVICE_ACCOUNT_FILE(path to service account JSON — host-specific, not a secret itself)JAVA_HOME(not a secret, but host-specific — verify Java path on target)
Note cron jobs and scheduled tasks
# macOS: log rotation config
cat /etc/newsyslog.d/openclaw.conf 2>/dev/null
# macOS: check for any openclaw-related cron entries
sudo crontab -l 2>/dev/null | grep -i openclaw
sudo -u openclaw crontab -l 2>/dev/null
# Linux: log rotation
cat /etc/logrotate.d/openclaw 2>/dev/null
# Linux: cron/timers
sudo crontab -l 2>/dev/null | grep -i openclaw
sudo -u openclaw crontab -l 2>/dev/null
systemctl list-timers | grep openclawAlso check for:
- Session pruning —
find ... -mtime +30 -deletecommands for old session files - Temp file cleanup — image-gen plugin saves to
$TMPDIR/openclaw-image-gen/; may have a cleanup cron - Workspace git sync — automated
git pushschedules for workspace backups - Certificate renewal — if using custom TLS termination
Step 2 — Set Up the Target
Install dependencies
macOS (Apple Silicon):
# Homebrew + Node.js
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install node
# Docker (OrbStack recommended)
brew install --cask orbstack
# Signal support (if needed)
brew install signal-cli # requires Java 21Linux:
sudo apt update && sudo apt upgrade -y
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
curl -fsSL https://get.docker.com | sudo shCreate the dedicated user
Follow the Dedicated OS User instructions from Phase 6.
sysadminctl doesn’t create home directories (macOS). After
sysadminctl -addUser, you mustsudo mkdir -p /Users/openclaw && sudo chown openclaw:staff /Users/openclaw.
Install OpenClaw
# Global install (admin user)
curl -fsSL https://openclaw.ai/install.sh | bash
# Verify the dedicated user can access it
sudo -u openclaw openclaw --versionBuild the sandbox image
If using Docker sandboxing, build the sandbox image on the target. The image is not transferred in the backup — it must be built locally from the OpenClaw install:
cd $(npm root -g)/openclaw
./scripts/sandbox-setup.sh
# Verify
docker run --rm openclaw-sandbox:bookworm-slim git --versionIf you use a custom sandbox image, see Custom Sandbox Images: Multi-Machine Deployment — either push to a private registry or use
docker save/docker loadto transfer it.
Step 3 — Transfer Data
Copy the backup to target
# Via scp (over Tailscale or local network)
scp openclaw-backup-*.tar.gz user@target-host:/tmp/
# Or via rsync for large backups with resume support
rsync -avz --progress openclaw-backup-*.tar.gz user@target-host:/tmp/Secure the backup on target — it contains credentials. Restrict access immediately after transfer:
chmod 600 /tmp/openclaw-backup-*.tar.gz
Extract to the new user’s home
# macOS — extract as the openclaw user
sudo mkdir -p /Users/openclaw/.openclaw
sudo tar xzf /tmp/openclaw-backup-*.tar.gz -C /Users/openclaw/
sudo chown -R openclaw:staff /Users/openclaw/.openclaw
# Linux
sudo mkdir -p /home/openclaw/.openclaw
sudo tar xzf /tmp/openclaw-backup-*.tar.gz -C /home/openclaw/
sudo chown -R openclaw:openclaw /home/openclaw/.openclawUpdate paths in config
If migrating between different home directories or OS:
# macOS → macOS (different username)
sudo -u openclaw sed -i '' 's|/Users/old-user|/Users/openclaw|g' \
/Users/openclaw/.openclaw/openclaw.json
# macOS → Linux
sudo -u openclaw sed -i 's|/Users/old-user|/home/openclaw|g' \
/home/openclaw/.openclaw/openclaw.json
# Linux → macOS
sudo -u openclaw sed -i '' 's|/home/old-user|/Users/openclaw|g' \
/Users/openclaw/.openclaw/openclaw.json
sed -idiffers by OS. On macOS, usesed -i ''(empty quotes required). On Linux, usesed -i(no quotes). The examples above show the correct syntax for each platform.
Review the config manually after
sed. Automated path replacement can miss embedded paths or catch false positives. Openopenclaw.jsonand verify: workspace paths,agentDirpaths, extension paths,$includepaths, and any absolute paths in tool configurations all point to valid locations on the target.
Step 4 — Component-Specific Migration
Config (openclaw.json)
- Update all absolute paths to match the target host
- Verify
gateway.modeis set (gateway refuses to start without it) - Remove any stale/unrecognized keys — run
openclaw doctorto check - If the target has a different OpenClaw version, run
openclaw doctor --fixfor config migrations
Workspaces (memory, SOUL.md)
Workspaces (~/.openclaw/workspaces/<agent>/) contain the most important persistent data:
- SOUL.md — agent personality and instructions. Review and customize for the new deployment context (e.g., update host-specific references).
- Memory files — semantic memory, conversation summaries, learned facts. Transfer as-is.
- Research files — agent-generated research documents. Transfer as-is.
# Verify workspace files transferred
sudo -u openclaw ls -la /Users/openclaw/.openclaw/workspaces/main/WhatsApp credentials
WhatsApp uses Baileys for session management. The credential store (often hundreds of files) does transfer between hosts:
# Verify credential files exist
sudo -u openclaw ls /Users/openclaw/.openclaw/credentials/whatsapp/default/ | wc -l
registered: Falseincreds.jsonis misleading. This field doesn’t reliably indicate connection status. After starting the gateway, check logs forListening for personal WhatsApp inbound messages— that confirms the session is active.
Channel config matters too. Verify
channels.whatsapp.allowFromhas real phone numbers, not placeholders (+46XXXXXXXXX). Placeholder values cause silent message drops with no log warning.
Signal credentials
Signal credentials do not transfer between devices. You must re-pair on the new host:
sudo -u openclaw bash -c 'cd /Users/openclaw && HOME=/Users/openclaw signal-cli link -n "OpenClaw"'Scan the QR code from Signal > Settings > Linked Devices > Link New Device. See Signal Setup for the full process.
JAVA_HOME: After installing signal-cli via Homebrew, verify
JAVA_HOMEpoints to Java 21. Stale paths from previous Homebrew upgrades are a common cause of signal-cli failures. Set explicitly in your plist:JAVA_HOME=/opt/homebrew/Cellar/openjdk@21/<version>/libexec/openjdk.jdk/Contents/Home
Google Chat credentials
Google Chat uses a GCP service account for authentication. The service account JSON file does transfer between hosts, but paths and webhook URLs need updating.
- Copy the service account JSON — included in the backup if stored under
~/.openclaw/credentials/googlechat/ - Update the path in
GOOGLE_CHAT_SERVICE_ACCOUNT_FILEenv var to match the target host - Update the webhook URL — if the target has a different hostname or Tailscale node name, the audience in the Google Chat app configuration must change
- Re-enable Tailscale Funnel on the target (if used for webhook exposure):
tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechatUse path-scoped Funnel (
--set-path) to avoid exposing the entire gateway API to the internet. - Update the Chat app URL in the GCP Console to point to the new host’s public URL
See the Google Chat setup guide for the full configuration process.
Auth profiles
Auth profiles (agents/<id>/agent/auth-profiles.json) must exist for each agent that uses authenticated tools:
# Verify auth profiles are present for all agents
sudo -u openclaw find /Users/openclaw/.openclaw/agents -name "auth-profiles.json" -lsIf migrating from a single-agent to multi-agent setup (or vice versa), ensure each agent directory has a copy.
Extensions (plugins)
Plugin directories should be named to match their manifest ID (e.g., content-guard/, not openclaw-content-guard/):
# Verify plugin directories
sudo -u openclaw ls /Users/openclaw/.openclaw/extensions/
# Expected: channel-guard/ content-guard/ image-gen/ (matching manifest IDs)If node_modules weren’t included in the backup (they’re large), reinstall per plugin:
for plugin in channel-guard content-guard file-guard network-guard command-guard image-gen; do
[ -d "/Users/openclaw/.openclaw/extensions/$plugin" ] && \
sudo -u openclaw bash -c "cd /Users/openclaw/.openclaw/extensions/$plugin && npm install"
donechannel-guard and content-guard use OpenRouter. Ensure
OPENROUTER_API_KEYis set on the target host before first start or both guards fail closed.
Plugin changes require gateway restart. Unlike config changes which hot-reload, plugin source is loaded at startup only.
Memory search index
The memory search index uses architecture-dependent native binaries and may not be portable across hosts. Rebuild it after migration:
sudo -u openclaw bash -c 'cd /Users/openclaw && HOME=/Users/openclaw openclaw memory index'Memory data files (in workspaces) transfer fine — only the search index needs rebuilding. This command may run for several minutes with no visible progress output — this is normal. Do not interrupt it.
Session history (optional)
Session files (agents/<id>/sessions/*.jsonl) contain full conversation history including tool output. They can be large.
Recommended: Start fresh on the new host. Memory files (in workspaces) preserve the agent’s knowledge; sessions are just conversation logs.
If transferring sessions: Watch for poisoned sessions. A poisoned session is one where prompt injection has been stored in the conversation history — the injected content replays on every subsequent turn, potentially steering the agent’s behavior indefinitely. Sessions with malformed data (e.g., broken plugin output) are similarly problematic: every message to that conversation will fail with the same error. Delete affected session files:
# Find sessions with known broken patterns
sudo -u openclaw grep -rl "media_type" /Users/openclaw/.openclaw/agents/*/sessions/*.jsonl
# Delete any matchesStep 5 — Set Up Services
LaunchAgent / systemd
Create new service files on the target — don’t copy plists or unit files from the source, as they contain secrets and host-specific paths.
Follow the LaunchDaemon (macOS) or systemd instructions from Phase 6. For the LaunchAgent alternative (auto-login setups), see LaunchAgent .
Enter secrets
macOS: Add secrets to the new plist via PlistBuddy:
PLIST=/Users/openclaw/Library/LaunchAgents/ai.openclaw.gateway.plist
sudo -u openclaw /usr/libexec/PlistBuddy -c "Set :EnvironmentVariables:ANTHROPIC_API_KEY sk-ant-..." "$PLIST"
sudo -u openclaw /usr/libexec/PlistBuddy -c "Set :EnvironmentVariables:OPENCLAW_GATEWAY_TOKEN $(openssl rand -hex 32)" "$PLIST"
# ... repeat for each secret
sudo chmod 600 "$PLIST"Linux: Create the secrets environment file:
sudo mkdir -p /etc/openclaw
sudo tee /etc/openclaw/secrets.env > /dev/null << 'EOF'
ANTHROPIC_API_KEY=sk-ant-...
OPENCLAW_GATEWAY_TOKEN=<generate with openssl rand -hex 32>
BRAVE_API_KEY=...
GITHUB_TOKEN=github_pat_...
EOF
sudo chown root:root /etc/openclaw/secrets.env
sudo chmod 600 /etc/openclaw/secrets.envEmpty env vars cause startup failure. For optional keys not yet provisioned, use a non-empty placeholder like
"not-configured"— not an empty string.
OrbStack / Docker (macOS)
If using Docker sandboxing with OrbStack, ensure the engine is running and accessible:
orbctl start
# Verify docker works for the openclaw user
sudo -u openclaw /usr/local/bin/docker psOrbStack’s docker CLI is at /usr/local/bin/docker — this path may not be in the service user’s PATH. Use the full path or add it to the plist’s PATH environment variable.
Playwright (browser tool)
Install Chromium for the dedicated user:
sudo -u openclaw bash -c 'cd /Users/openclaw && HOME=/Users/openclaw npx -y playwright install chromium'Correct HOME and PATH required. The npm cache must be writable by the target user, and
cdto the user’s home is needed to avoidprocess.cwd()failures.
Step 6 — Recreate Scheduled Tasks
Log rotation
macOS — create /etc/newsyslog.d/openclaw.conf:
/Users/openclaw/.openclaw/logs/gateway.log openclaw:staff 640 7 1024 * J
/Users/openclaw/.openclaw/logs/gateway.err.log openclaw:staff 640 7 1024 * J
640restricts log access to owner and group only. Gateway logs may contain sensitive data.
Linux — create /etc/logrotate.d/openclaw:
/home/openclaw/.openclaw/logs/*.log {
weekly
rotate 4
compress
missingok
notifempty
}Session pruning
Recreate any session cleanup cron from the source:
# Example: delete sessions older than 30 days (daily at 3am)
sudo -u openclaw crontab -e
# Add: 0 3 * * * find /Users/openclaw/.openclaw/agents/*/sessions -name "*.jsonl" -mtime +30 -deleteTemp file cleanup
If using the image-gen plugin, add cleanup for generated temp images:
# Example: clean image-gen temp files older than 7 days (daily at 4am)
# Add to openclaw user's crontab:
0 4 * * * find /tmp/openclaw-image-gen -type f -mtime +7 -delete 2>/dev/nullmacOS clears
/tmpon reboot, so this is mainly needed for long-running Linux servers.
Workspace git sync
If you had automated workspace backup pushes, recreate on the target:
# Example: push workspace changes daily at midnight
# In openclaw user's crontab:
0 0 * * * cd /Users/openclaw/.openclaw/workspaces/main && git add -A && git commit -m "auto-backup $(date +\%Y-\%m-\%d)" && git push 2>/dev/nullEnsure
GITHUB_TOKENis available to the cron environment — either via the user’s.bashrcor set in the crontab withGITHUB_TOKEN=github_pat_...at the top.
Step 7 — File Permissions and Security
Lock down the target deployment:
macOS:
sudo chown -R openclaw:staff /Users/openclaw/.openclaw
sudo chmod 700 /Users/openclaw
sudo chmod 700 /Users/openclaw/.openclaw
sudo chmod 600 /Users/openclaw/.openclaw/openclaw.json
sudo chmod 600 /Users/openclaw/.openclaw/credentials/*.json
sudo chmod 600 /Users/openclaw/.openclaw/agents/*/agent/auth-profiles.json
sudo chmod 600 /Users/openclaw/.openclaw/identity/*.json
sudo chmod -R 600 /Users/openclaw/.openclaw/credentials/whatsapp/default/*
sudo chmod 700 /Users/openclaw/.openclaw/credentials/whatsappLinux:
sudo chown -R openclaw:openclaw /home/openclaw/.openclaw
sudo chmod 700 /home/openclaw
sudo chmod 700 /home/openclaw/.openclaw
sudo chmod 600 /home/openclaw/.openclaw/openclaw.json
sudo chmod 600 /home/openclaw/.openclaw/credentials/*.json
sudo chmod 600 /home/openclaw/.openclaw/agents/*/agent/auth-profiles.json
sudo chmod 600 /home/openclaw/.openclaw/identity/*.json
sudo chmod -R 600 /home/openclaw/.openclaw/credentials/whatsapp/default/*
sudo chmod 700 /home/openclaw/.openclaw/credentials/whatsappAdmin access (optional)
If you need to manage the service user’s files from your admin account:
# Traverse-only on home dir
sudo chmod +a "youradmin allow list,search,execute" /Users/openclaw
# Full read+write with inheritance on .openclaw
sudo chmod -R +a "youradmin allow read,write,append,readattr,writeattr,readextattr,writeextattr,readsecurity,list,search,execute,delete,add_file,add_subdirectory,delete_child,file_inherit,directory_inherit" /Users/openclaw/.openclawFirewall
Re-enable firewall on the target:
# macOS
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode on
# Linux
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enableTailscale (if applicable)
If using Tailscale for secure remote access or Funnel for webhook exposure:
# Authenticate on target
tailscale up
# Re-tag the device in Tailscale admin console or via CLI
# Verify ACLs allow traffic from expected sources
tailscale statusRe-tag the new device as tag:openclaw in the Tailscale admin console. If using Tailscale Funnel for Google Chat webhooks, re-enable it on the target (see Google Chat setup
).
Step 8 — Verify
Start the gateway
# Create log directory first
sudo -u openclaw mkdir -p /Users/openclaw/.openclaw/logs
# macOS (LaunchDaemon — default for dedicated headless accounts)
sudo launchctl bootstrap system /Library/LaunchDaemons/ai.openclaw.gateway.plist
# macOS (LaunchAgent — only if openclaw user has auto-login or active GUI session)
# sudo launchctl bootstrap gui/$(id -u openclaw) /Users/openclaw/Library/LaunchAgents/ai.openclaw.gateway.plist
# Linux
sudo systemctl enable --now openclaw-gatewayVerification checklist
# Gateway is running
sudo launchctl print system/ai.openclaw.gateway 2>&1 | head -10 # macOS (LaunchDaemon)
sudo systemctl status openclaw-gateway # Linux
# Listening on correct port
sudo lsof -i :18789
# Health check
curl -s -H "Authorization: Bearer <token>" http://127.0.0.1:18789/health
# Config validation
sudo -u openclaw bash -c 'cd /Users/openclaw && HOME=/Users/openclaw openclaw doctor'
# Recent logs (look for errors)
tail -50 /Users/openclaw/.openclaw/logs/gateway.log- Gateway starts and stays running (check logs for crash loops)
- Health endpoint responds
- WhatsApp messages get responses (check for
Listening for personal WhatsApp inbound messagesin logs) - Signal messages get responses (if re-paired)
- Google Chat messages get responses (if configured — check webhook delivery in GCP console)
- Memory search works (
openclaw memory search <query>— runopenclaw memory indexfirst if no results) -
openclaw security auditreturns no critical findings - Sandbox image built on target (
docker image inspect openclaw-sandbox:bookworm-slim— if missing:cd $(npm root -g)/openclaw && ./scripts/sandbox-setup.sh) - Docker sandboxing active (if applicable —
docker psshows sandbox containers) - File permissions are correct (600/700 on sensitive files)
- Gateway only listens on loopback
- Log rotation config in place
- Cron jobs / scheduled tasks recreated
- Tailscale device tagged as
tag:openclawand ACLs applied (if applicable)
Clean up
Before stopping the source service, verify on the target:
- Gateway starts and responds to health checks
- All agents load without errors
- Channel connections establish (check logs)
- Memory search returns expected results
Consider keeping the source running for 24 hours after target verification.
After verifying everything works:
# Remove backup from /tmp on target
rm /tmp/openclaw-backup-*.tar.gz
# On source: keep the backup, stop and disable the old service
sudo launchctl bootout system/ai.openclaw.gateway # macOS (LaunchDaemon)
sudo systemctl disable --now openclaw-gateway # LinuxKeep the source backup for at least a few weeks. Channel sessions (especially WhatsApp) can silently fail days later if the credential migration has subtle issues.
Multi-Instance Migration
For multi-gateway deployments , repeat the process per instance. This applies to all approaches — profiles, multi-user, and VM variants. Each instance has:
- Its own OS user (e.g.,
openclaw-bob,openclaw-tibra) - Its own
.openclaw/directory, config, and secrets - Its own LaunchAgent (or LaunchDaemon) with a unique label and port
- Its own cron jobs for log rotation, session pruning, etc.
Migrate instances independently — they share no state. The only shared components are host-level installs (Node.js, OrbStack, signal-cli).
VM Deployment Migration
If running OpenClaw inside a VM rather than directly on the host, you can migrate the entire VM image instead of individual files.
macOS VMs
Lume:
# On source host — export the VM
lume export openclaw-vm -o openclaw-vm.tar.gz
# Transfer to target host
scp openclaw-vm.tar.gz user@target-host:/tmp/
# On target host — import
lume import /tmp/openclaw-vm.tar.gz --name openclaw-vm
lume start openclaw-vmParallels:
# On source host — clone/export (stop VM first)
prlctl stop openclaw-vm
prlctl clone openclaw-vm --name openclaw-vm-export
# Transfer the .pvm bundle to target host
rsync -avz ~/Parallels/openclaw-vm-export.pvm/ user@target-host:~/Parallels/openclaw-vm.pvm/
# On target host — register and start
prlctl register ~/Parallels/openclaw-vm.pvm
prlctl start openclaw-vmLinux VMs
Multipass:
# Transfer files into/out of VM
multipass transfer openclaw-vm:/home/openclaw/.openclaw/openclaw.json ./backup/
# Or snapshot + restore approach
multipass stop openclaw-vm
# Copy the VM image from /var/snap/multipass/common/data/multipassd/vault/instances/KVM / libvirt:
# On source host — export disk image
virsh shutdown openclaw-vm
virsh vol-download --pool default openclaw-vm.qcow2 /tmp/openclaw-vm.qcow2
# Transfer to target host
rsync -avz --progress /tmp/openclaw-vm.qcow2 user@target-host:/tmp/
# On target host — import
virsh vol-create-as --pool default --name openclaw-vm.qcow2 --format qcow2 --capacity 0
virsh vol-upload --pool default openclaw-vm.qcow2 /tmp/openclaw-vm.qcow2
# Re-define the domain XML (update paths as needed)Post-VM-transfer steps
After importing a VM image on the target host:
- Update networking — VM may get a new IP; update any Tailscale/firewall rules on the host
- Re-authorize Tailscale inside the VM (if used) —
tailscale up, re-tag astag:openclaw - Inside-VM migration follows the same file-level steps above (path updates, secrets, permissions) if the VM’s internal layout changed
- Docker volumes (Linux VMs with Docker inside) — included in the VM image; verify with
docker volume ls
Troubleshooting
Gateway exits immediately after start
Run openclaw doctor to diagnose. Common causes:
- Missing
gateway.modein config - Empty env var (use
"not-configured"as placeholder for optional keys) - Unrecognized config keys from a newer/older OpenClaw version — run
openclaw doctor --fix - Plugin directories missing or misnamed (must match manifest ID)
WhatsApp messages silently dropped
Check channels.whatsapp.allowFrom — placeholder values (+46XXXXXXXXX) cause silent drops with no log warning. Replace with real phone numbers.
Signal won’t connect
Signal credentials don’t transfer. Re-pair on the new host: sudo -u openclaw signal-cli link -n "OpenClaw". Also verify JAVA_HOME points to Java 21.
“process.cwd() failed” errors
Running commands as the service user from a directory that user can’t access. Always use:
sudo -u openclaw bash -c 'cd /Users/openclaw && HOME=/Users/openclaw <command>'Plugin errors persist after fix
Broken tool results in session history replay on every message. Delete the affected session file:
# Identify poisoned sessions
sudo -u openclaw grep -rl "<error pattern>" /Users/openclaw/.openclaw/agents/*/sessions/
# Delete and restart gatewayMemory search returns no results
The search index is architecture-dependent and may not be portable between hosts. Rebuild it:
sudo -u openclaw bash -c 'cd /Users/openclaw && HOME=/Users/openclaw openclaw memory index'This can take a few minutes for large memory stores. The gateway doesn’t need to be stopped.
Next Steps
Your migrated deployment should now be fully operational. If anything seems off, the troubleshooting section above covers the most common post-migration issues.
Reference — config cheat sheet, tool list, gotchas