# Monthly Maintenance Checklist — Hermes + Debian AI Server

**Created:** 2026-06-18  
**Machine:** debian-ai (192.168.1.50, Debian 13, RTX 2080 Ti 11GB)  
**Estimated time:** 15-20 minutes per month

---

## 1. Hermes Health (5 min)

### 1a. Gateway + Dashboard
```bash
systemctl --user is-active hermes-gateway hermes-dashboard
systemctl --user status hermes-gateway --no-pager | tail -5
curl -s http://localhost:18789/health
curl -s http://localhost:9119/api/status | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'Gateway: {d[\"gateway_state\"]}, Auth: {d[\"auth_required\"]}')"
```

**Expected:** gateway=running, auth_required=false

### 1b. Version Check
```bash
hermes --version
```
**Action if behind:** `hermes update` (wait for completion, then verify)

### 1c. Config Validity
```bash
hermes config check 2>&1 | grep -E "error|warning|invalid" || echo "OK"
hermes doctor 2>&1 | tail -10
```

### 1d. DB Integrity
```bash
python3 -c "
import sqlite3
for db in ['~/.hermes/state.db', '~/.hermes/kanban.db']:
    import os
    path = os.path.expanduser(db)
    if os.path.exists(path):
        c = sqlite3.connect(path).cursor()
        c.execute('PRAGMA integrity_check')
        print(f'{db}: {c.fetchone()[0]}')
"
```

### 1e. Cron Health
```bash
cat ~/.hermes/cron/jobs.json | python3 -c "
import sys,json
jobs = json.load(sys.stdin).get('jobs',[])
for j in jobs:
    status = 'ACTIVE' if j.get('enabled') else 'PAUSED'
    last = j.get('last_status','?')
    print(f'{j[\"name\"]}: {status}, last_run={last}')
"
```

### 1f. Memory Usage
```bash
wc -c ~/.hermes/memories/USER.md ~/.hermes/memories/MEMORY.md
```
**Thresholds:** USER.md < 2,500 chars, MEMORY.md < 3,000 chars

---

## 2. Model Health (3 min)

### 2a. llama-swap Proxy
```bash
systemctl is-active llama-swap
curl -s http://localhost:9292/v1/models | python3 -c "import sys,json; models=json.load(sys.stdin)['data']; print(f'Models: {len(models)}')"
```

**Expected:** 7 models

### 2b. Model Files
```bash
find /models -name "*.gguf" -not -path "*/archive/*" -not -path "*/embeddings/*" -exec ls -lh {} \; | awk '{print $5, $NF}'
```
**Check for:** Unexpectedly small files (<1GB GGUF = partial download), unexpected new files

### 2c. VRAM Check
```bash
nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader
```

### 2d. Systemd Services
```bash
sudo systemctl is-active llama-swap
# For each local model service:
systemctl --user list-units --type=service | grep llama-server
```

---

## 3. Docker Health (2 min)

### 3a. Container Status
```bash
docker ps --format '{{.Names}} {{.Status}}'
```
**Expected:** open-webui (healthy), nexstream-scraper (healthy)

### 3b. Image Cleanup
```bash
docker image ls --format '{{.Repository}}:{{.Tag}} {{.Size}}'
docker image prune -f 2>/dev/null  # Remove unused images
docker container prune -f 2>/dev/null  # Remove stopped containers
```

### 3c. Disk Usage
```bash
docker system df
```

---

## 4. Storage & Logs (3 min)

### 4a. Disk Space
```bash
df -h / /models
du -sh ~/ ~/archive/ 2>/dev/null
```

### 4b. Log Rotation
```bash
du -sh ~/.hermes/logs/
# Delete logs older than 30 days:
find ~/.hermes/logs -name "*.log.*" -mtime +30 -delete
find ~/.hermes/logs -name "*.log" -size +10M -exec truncate -s 0 {} \;
```

### 4c. Package Cleanup
```bash
sudo apt autoremove --purge -y
sudo apt clean
```

### 4d. Journal Cleanup
```bash
journalctl --disk-usage
sudo journalctl --vacuum-time=30d
```

### 4e. Broken Symlinks
```bash
find ~ -xtype l -not -path '*/.git/*' -not -path '*/node_modules/*' 2>/dev/null | wc -l
echo "broken symlinks (0 expected)"
```

---

## 5. Scripts & Skills (2 min)

### 5a. Active Skills
```bash
python3 -c "
import yaml,os
with open(os.path.expanduser('~/.hermes/config.yaml')) as f:
    cfg=yaml.safe_load(f)
disabled = set(cfg.get('skills',{}).get('disabled',[]))
count = 0
for root,dirs,files in os.walk(os.path.expanduser('~/.hermes/skills')):
    if 'SKILL.md' in files and os.path.basename(root) not in disabled:
        count+=1
print(f'Active skills: {count} (target: 20-30)')
"
```

### 5b. Active Scripts
```bash
ls -la ~/.hermes/scripts/*.py
```
**Verify:** repo-map.py, auto-model-switch.py, model-health-watchdog.py, models.py present

### 5c. RepoMap Test
```bash
timeout 15 python3 ~/.hermes/scripts/repo-map.py ~/Unspooled --budget 1024 > /dev/null 2>&1 && echo "✅" || echo "⚠️ timeout"
```

---

## 6. Config Backups (1 min)

### 6a. Config Snapshot
```bash
cp ~/.hermes/config.yaml ~/archive/configs/config-$(date +%Y%m%d).yaml
```
**Keep:** Last 3 monthly backups, delete older ones

---

## 7. Quick Health Summary Command

Run this single command for a 30-second overview:

```bash
echo "=== Hermes ===" && systemctl --user is-active hermes-gateway && \
echo "=== llama-swap ===" && curl -s http://localhost:9292/v1/models | python3 -c "import sys,json;print(f'{len(json.load(sys.stdin)[\"data\"])} models')" && \
echo "=== Docker ===" && docker ps --format '{{.Names}}: {{.Status}}' && \
echo "=== Disk ===" && df -h / /models | tail -2
```

---

## Annual Tasks

1. **VACUUM state.db**: `sqlite3 ~/.hermes/state.db 'VACUUM; PRAGMA wal_checkpoint(TRUNCATE);'`
2. **Full skill audit**: Review disabled skills list, prune truly obsolete ones
3. **Model download check**: Verify all model files match their HuggingFace source
4. **Docker deep clean**: `docker system prune -a` (removes all unused images/volumes — use cautiously)
5. **Configuration review**: Check for deprecated config keys, stale provider references
