# Server Migration — Complete Step-by-Step Checklist

## ⚠️ Core Principles (Blood and Tears Edition)

1. **First confirm what machine you're on** — run `hostname && ip addr show | grep 'inet '`
2. **Set up new server first, then copy config** — never operate both simultaneously
3. **Exclude large files from tar** — `.hermes/` can be 700MB+ from node_modules/sessions-state-snapshots
4. **One step at a time** — verify each step before moving on

## Step 1: New Server Preparation

```bash
# Check Python version
sshpass -p 'password' ssh -o StrictHostKeyChecking=no root@NEW_IP 'python3 -V'

# Install venv (Ubuntu 26.04: python3.14-venv)
sshpass -p 'password' ssh root@NEW_IP 'apt install -y python3.14-venv'

# Create venv and install Hermes
sshpass -p 'password' ssh root@NEW_IP 'python3 -m venv /opt/hermes-venv && /opt/hermes-venv/bin/pip install -q hermes-agent'

# Verify
sshpass -p 'password' ssh root@NEW_IP '/opt/hermes-venv/bin/hermes --version'
```

## Step 2: Pack Old Server (Exclude Large Files!)

```bash
cd /root && tar czf /tmp/hermes-migrate.tar.gz \
  .hermes/config.yaml \
  .hermes/config.yaml.bak.* \
  .hermes/.env \
  .hermes/SOUL.md \
  .hermes/skills/ \
  .hermes/state.db \
  .hermes/state.db-wal \
  .hermes/state.db-shm \
  .hermes/sessions/sessions.json \
  .hermes/logs/ \
  .hermes/bin/ \
  .hermes/cache/ \
  .hermes/cron/ \
  .hermes/kanban.db \
  .hermes/models_dev_cache.json \
  .hermes/webchat/ \
  .hermes/pastes/ \
  .hermes/response_store.db

ls -lh /tmp/hermes-migrate.tar.gz  # Should be < 100MB
```

## Step 3: Transfer to New Server

```bash
# Critical files (SOUL.md, sessions.json, state.db — ALL critical!)
sshpass -p 'new_password' scp -o StrictHostKeyChecking=no \
  /root/.hermes/SOUL.md \
  /root/.hermes/config.yaml \
  /root/.hermes/.env \
  /root/.hermes/state.db \
  /root/.hermes/state.db-wal \
  /root/.hermes/state.db-shm \
  /root/.hermes/sessions/sessions.json \
  root@NEW_IP:/root/.hermes/

# Skills (use rsync — too many files for scp)
rsync -avz --timeout=60 -e "sshpass -p 'new_password' ssh -o StrictHostKeyChecking=no" \
  /root/.hermes/skills/ root@NEW_IP:/root/.hermes/skills/
```

## Step 4: Start Gateway on New Server

```bash
sshpass -p 'password' ssh root@NEW_IP '
pkill -f "hermes" 2>/dev/null
sleep 2
cd /root
nohup /opt/hermes-venv/bin/python -m hermes_cli.main gateway run --replace > /tmp/hermes-gateway.log 2>&1 &
echo "PID: $!"
sleep 8
cat /tmp/hermes-gateway.log
'
```

## Step 5: Post-Migration Fixes

### Must-Do Fixes:

```bash
# 1. Copy SOUL.md to AGENTS.md (Hermes reads /root/AGENTS.md!)
cp /root/.hermes/SOUL.md /root/AGENTS.md

# 2. Check personality in config.yaml
sed -i 's/personality: kawaii/personality: helpful/' /root/.hermes/config.yaml

# 3. Create hermes softlink
ln -sf /opt/hermes-venv/bin/hermes /usr/local/bin/hermes

# 4. TTS voice — default is English, change to Chinese for 张哥
# In config.yaml tts.edge.voice: zh-CN-XiaoxiaoNeural

# 5. Install video tools
apt install -y ffmpeg
/opt/hermes-venv/bin/pip install yt-dlp Pillow
```

### Common Issues After Migration:

| Issue | Fix |
|-------|-----|
| "No messaging platforms enabled" | Missing `sessions.json` — copy it from old server |
| QQ bot replies as "OWL" instead of persona | Missing `SOUL.md` or `AGENTS.md` — must copy both |
| No user memory | Missing `state.db*` — copy from old server |
| kanban.db errors | Can delete — gateway recreates it: `rm ~/.hermes/kanban.db` |
| Duplicate QQ replies | Two gateway processes running — `pkill -f hermes` and restart |

## Step 6: Verify

```bash
sshpass -p 'password' ssh root@NEW_IP '
ps aux | grep hermes | grep -v grep
cat ~/.hermes/gateway_state.json 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get(\"platforms\",{}))"
'
```

## Step 7: Clean Up

```bash
rm /tmp/hermes-migrate.tar.gz
sshpass -p 'password' ssh root@NEW_IP 'rm /tmp/hermes-migrate.tar.gz'
```

## ⚠️ Shutdown Old Server Carefully

- Use **SHUTDOWN** (not DESTROY) — keeps data recoverable
- Old server running = QQ messages get two replies (张哥 hates this)
- After SHUTDOWN, send a test message to confirm only one reply comes back
- If unsure: REBOOT and SHUTDOWN again
