---
title: Stratégie de backup DAEMON
type: infra
created: 2026-04-18
updated: 2026-04-19
owner: jerem
status: ✅ déployé et opérationnel
tags:
  - infra
  - backup
  - kopia
  - security
related:
  - "[[_adn/infra/vps-config-2026-04-17]]"
  - "[[_adn/soul]]"
---

# Stratégie de backup DAEMON — 2026-04-18

> **État (2026-04-19)** : ✅ Déployé. 3 destinations actives (local VPS + Google Drive + NAS Ugreen). Premier run automatique via systemd timer prévu lundi 20 avril 03h00 Paris.

---

## 🎯 Objectif

Protéger les données critiques de l'écosystème DAEMON (vault Obsidian, data Notion, configs infrastructure) contre :
- Panne matérielle VPS
- Corruption / suppression accidentelle
- Compromission sécurité (ransomware, intrusion)
- Perte compte iCloud / Google
- Faillite / coupure d'un fournisseur

Règle appliquée : **3-2-1** (3 copies, 2 médias différents, 1 offsite minimum). En réalité on vise **3-3-2** : 3 destinations de backup, 3 médias distincts, 2 offsites.

---

## 📊 Ce qui est backupé (par criticité)

### Tier 1 — catastrophique si perdu (backup quotidien)

| Source | Contenu | Pourquoi critique |
|---|---|---|
| `/home/jerem/vault/` | Vault Obsidian (cerveau DAEMON) | Contient toute la mémoire, les règles, l'historique |
| `/home/jerem/notion-backup/` | Export Notion quotidien en Markdown | Clients, projets, habitudes, journaling |

### Tier 2 — reinstallable mais douloureux (backup hebdomadaire le dimanche)

| Source | Contenu |
|---|---|
| `/home/jerem/.openclaw/` | Config OpenClaw, credentials Google/Gmail |
| `/home/jerem/daemon-infra/` | Scripts, systemd units, config nginx/wireguard |
| `/etc/systemd/system/` | Services systemd |
| `/etc/nginx/` | Config reverse proxy |
| `/etc/wireguard/` | Tunnel VPN |

### Tier 3 — pas backupé

- Logs OpenClaw
- Caches, sessions
- Dossiers `/tmp/`, `/var/cache/`
- Anything ephemeral

### Non backupé volontairement

- **Doppler** : MFA activé, régénération possible en 4h. Risque accepté.
- **Bot tokens Slack/WhatsApp** : stockés dans Doppler, idem ci-dessus.

---

## 🗺️ Topologie

```
                   /home/jerem/vault (live VPS)
                               │
        ┌──────────────────────┼──────────────────────┐
        │                      │                      │
        ▼                      ▼                      ▼
  Kopia local          Kopia NAS ami (WebDAV)   Kopia Google Drive
  /var/backups/        nasprodfab.duckdns       gdrive jerem
  7 jours rétention    5 ans rétention          5 ans rétention
        │                      │                      │
        └── restore rapide     └── offsite physique   └── offsite cloud
```

**Pas d'offsite** : Kopia local (même machine que la source).
**Offsite physique** : NAS Ugreen de Fab.
**Offsite cloud** : Google Drive (compte jerem, 5TB).

---

## 🔒 Sécurité

### Chiffrement

- **Côté client, avant transit** : Kopia AES-256-GCM
- Le NAS de Fab voit uniquement des blobs chiffrés illisibles
- Google Drive idem

### Gestion de la passphrase Kopia

La passphrase de chiffrement est stockée à **3 endroits** :

1. **VPS** : `/root/.kopia-password` (mode `600`, root-only)
2. **Bitwarden** : entrée "Kopia DAEMON backup passphrase"
3. **Papier** : dans le coffre physique de Jerem

**Si VPS détruit + Bitwarden perdu → backups restaurables via la copie papier.**
**Si tous les 3 sont perdus → backups illisibles. Risque accepté : probabilité extrêmement faible.**

### Secrets NAS (WebDAV)

Stockés dans **Doppler** `daemon-infra/prd` :

- `NAS_WEBDAV_URL`
- `NAS_WEBDAV_USER`
- `NAS_WEBDAV_PASS`

Injectés dans l'environnement Kopia au moment du backup par le script de démarrage, jamais écrits en clair sur disque.

### Secrets Google Drive

Utilise l'OAuth Google déjà configuré pour Calendar/Gmail. Refresh token dans Doppler : `GOOGLE_REFRESH_TOKEN`.

---

## ⏰ Planning

| Quand | Quoi | Où |
|---|---|---|
| Chaque nuit à **3h00 Paris** (heure VPS fixe, ne change jamais en voyage) | Snapshot Kopia Tier 1 + 2 | Local VPS + NAS + gdrive |
| Chaque dimanche **3h30** | Snapshot Tier 2 seul si rien de Tier 1 n'a tourné | Idem |
| Chaque lundi **4h00** | **Test de restore automatique** (extraction d'un snapshot random vers `/tmp`, comparaison hash) | Local |

**Pourquoi 3h Paris fixe** : le backup tourne sur le VPS (pas sur le Mac), donc l'heure physique de Jerem importe peu. 3h Paris = toujours pendant une période de faible activité chez lui, peu importe le fuseau.

---

## 📦 Rétention GFS (Grandfather-Father-Son)

Kopia applique automatiquement :

| Horizon | Snapshots gardés |
|---|---|
| 7 derniers jours | Tous (1 par jour) |
| 4 dernières semaines | 1 par semaine |
| 12 derniers mois | 1 par mois |
| 5 dernières années | 1 par an |

**Total théorique** : ~28 snapshots actifs à n'importe quel instant.
**Taille réelle** : grâce à la déduplication Kopia (chunks unique stockés une seule fois), ~150-300 MB pour le repo complet, pas 28× la taille du vault.

---

## 🔄 Pipeline de backup (nightly 3h)

```
┌──────────────────────────────────────────────────────────────┐
│  0. Pull secrets Doppler (NAS_*, GOOGLE_*)                   │
│                                                              │
│  1. Notion export                                            │
│     ├─ notion-export → /home/jerem/notion-backup/*.md        │
│     ├─ git commit + push (repo Gitea dédié)                  │
│     ├─ SI ÉCHEC : alerte Slack + continue (export J-1 dispo) │
│     └─ ping Kuma push monitor "notion-export-ok"             │
│                                                              │
│  2. Kopia snapshot local                                     │
│     └─ sources : vault, notion-backup, daemon-infra,         │
│        .openclaw, /etc/* (hebdo seulement)                   │
│                                                              │
│  3. Kopia sync vers NAS ami (WebDAV)                         │
│     └─ SI ÉCHEC (NAS offline) : alerte + continue            │
│                                                              │
│  4. Kopia sync vers Google Drive                             │
│     └─ SI ÉCHEC : alerte + continue                          │
│                                                              │
│  5. Kopia maintenance (GFS prune, compaction)                │
│                                                              │
│  6. Ping Kuma "daemon-backup-ok"                             │
└──────────────────────────────────────────────────────────────┘
```

**Philosophie** : jamais d'arrêt brutal. Une étape qui rate alerte et laisse les autres continuer.

---

## 🚨 Monitoring et alertes

### Uptime Kuma Push Monitors

| Monitor | Seuil | Action si KO |
|---|---|---|
| `daemon-backup-ok` | Pas de ping en 25h | Alerte Slack (normal) |
| `notion-export-ok` | Pas de ping en 25h | J1 Slack normal · J2 Slack urgent + email · J3 appel téléphonique |
| `kopia-local-integrity` | Check intégrité hebdo | Slack urgent |
| `restore-test-ok` | Test restore hebdo | Slack normal si échec, urgent si 2× consécutif |

### Cascade d'escalade (conforme [[_adn/soul]] section 2)

| Palier | Canal | Niveau Kuma |
|---|---|---|
| Échec J1 | Slack DM | Normal |
| Échec J2 | Slack all devices ring + email | Urgent |
| Échec J3 | Appel téléphonique + tout ce qui précède | Très urgent |

---

## 🖥️ Interface Kopia UI

- **URL** : `https://kopia.daemon.jeremunlimited.com`
- **Auth** : basic auth + token header (pattern identique à Control UI OpenClaw)
- **Headers** : `X-Robots-Tag: noindex`
- **Accès** : public HTTPS ou LAN via WireGuard
- **Permissions** : lecture + restore uniquement (création snapshots bloquée en UI, seul le cron peut créer)

Utilisations :
- Voir les snapshots passés et leur taille
- Restaurer un fichier en 2 clics
- Vérifier l'intégrité d'un snapshot
- Monitorer l'espace disque des repos

---

## 🛠️ Commandes utiles (CLI)

```bash
# Lister les snapshots locaux
kopia snapshot list

# Restaurer la dernière version du vault
kopia snapshot restore latest /tmp/restore-vault

# Restaurer un fichier spécifique d'un snapshot donné
kopia snapshot restore <snapshot-id>/path/to/file /tmp/restored-file

# Vérifier l'intégrité
kopia snapshot verify

# Voir l'espace utilisé
kopia content stats

# Purger manuellement (suit la policy GFS)
kopia policy set --keep-latest 7 --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --keep-annual 5

# Forcer un backup immédiat
sudo systemctl start daemon-backup.service
```

---

## 📝 Plan de restauration (runbook)

### Scénario 1 : j'ai supprimé un fichier du vault

1. Ouvrir l'UI Kopia
2. Naviguer dans le dernier snapshot → `vault/`
3. Clic droit sur le fichier → Restore → `/home/jerem/vault/`
4. `git add` + commit dans le vault

### Scénario 2 : le VPS est HS, il faut tout reconstruire

1. Déployer un nouveau VPS (Hetzner, même config)
2. Installer Kopia
3. Restaurer la passphrase depuis Bitwarden (ou papier)
4. Connecter Kopia au repo Google Drive (plus rapide que NAS distant)
   ```bash
   kopia repository connect gdrive \
     --folder-id $FOLDER_ID \
     --credentials-file /path/to/oauth.json
   ```
5. `kopia snapshot restore latest --target /`
6. Reconfigurer secrets (régénérer OAuth Google, Doppler tokens)
7. Relancer systemd : `systemctl start openclaw`

**Temps cible de restauration complète** : < 4h (avec config nginx + wireguard + openclaw à reconfigurer).

### Scénario 3 : compromission sécurité, besoin de rollback

1. Identifier la date de compromission depuis les logs
2. Restaurer le snapshot de la veille (avant compromission)
3. `kopia snapshot restore <snapshot-pre-compromission> --target /home/jerem/`
4. Rotation de tous les secrets (Doppler → regenerate)
5. Invalider tous les OAuth Google / Slack / Notion tokens
6. Recréer depuis zéro les tokens

---

## 📐 Dimensionnement estimé

| Donnée | Taille actuelle | Taille dans 5 ans (×10) |
|---|---|---|
| Vault Obsidian | ~10 Mo | ~100 Mo |
| Notion export mirror | ~50 Mo | ~500 Mo |
| daemon-infra | ~5 Mo | ~50 Mo |
| .openclaw config | ~2 Mo | ~20 Mo |
| /etc/* (tier 2) | ~10 Mo | ~10 Mo (stable) |
| **Total source** | **~77 Mo** | **~680 Mo** |
| **Total repo Kopia (28 snapshots + dedup + compression)** | **~200 Mo** | **~1.5 Go** |

**Quota à demander à Fab** : 10 Go (avec marge confortable).
**Usage Google Drive** : 10 Go sur 5 TB (0.2 %).
**Usage VPS local** : ~200 Mo (7 jours de rétention, largement acceptable).

---

## ✅ Déploiement effectué (2026-04-19)

- [x] **Validation Fab** : dossier `Jerem Unlimited Création de contenus/DAEMON-Backup` créé
- [x] **Passphrase Kopia** : générée + stockée Doppler (`KOPIA_PASSPHRASE`) + Bitwarden + papier
- [x] **Secrets NAS Doppler** : `NAS_WEBDAV_URL`, `NAS_WEBDAV_USER`, `NAS_WEBDAV_PASS` (rclone-obscured)
- [x] **OAuth Google unifié** : nouveau client Web (Calendar + Gmail + Drive scopes)
- [x] **Repo Kopia local** : `/var/backups/kopia-local` actif
- [x] **Sync Kopia → gdrive** : `gdrive:DAEMON-Backups/kopia` actif
- [x] **Sync Kopia → NAS** : `nas:Jerem Unlimited Création de contenus/DAEMON-Backup/kopia` actif
- [x] **Script** : `/home/jerem/daemon-infra/scripts/daemon-backup.sh`
- [x] **Systemd timer** : `daemon-backup.timer` (cron 03:00 Europe/Paris, persistent=true)
- [x] **Kopia UI** : `https://kopia.jeremunlimited.com` (basicAuth + noindex via Traefik/Coolify)
- [x] **Service systemd Kopia** : `kopia-server.service` (port 51515 sur 0.0.0.0, UFW restreint à 10.0.0.0/8)
- [x] **Monitoring Kuma** : Push Monitor `DAEMON Backup`, URL dans Doppler `KUMA_BACKUP_PUSH_URL`
- [x] **Sudoers rsync** : `/etc/sudoers.d/daemon-backup-rsync` (jerem peut rsync /etc/* sans password)

## 🔜 À faire ensuite

- [ ] **Test de restore réel** sur VPS blank (exercice trimestriel — premier exo Q3 2026)
- [ ] **Notion export** (Phase 2.6) : intégration script Python notion-sdk-py + commit Gitea `daemon-notion-mirror`
- [ ] **Monitoring sync vault Obsidian** (vérifier Git push toutes les minutes ne rate pas)

## 🔧 Notes techniques de déploiement

**TLS NAS** : le certificat NAS est auto-signé (duckdns). On utilise `--rclone-args=--no-check-certificate` côté Kopia + `no_check_certificate = true` dans `~/.config/rclone/rclone.conf`. Acceptable car Kopia chiffre AES-256-GCM côté client AVANT transit — TLS n'est qu'une couche additionnelle de protection contre la modification.

**Préservation rclone.conf** : le script `daemon-backup.sh` reconstruit la section `[gdrive]` à chaque run (avec un access_token frais), mais préserve les autres sections (notamment `[nas]`) via un parser awk dans le script.

**Permissions /etc/*** : impossible de snapshoter `/etc/{systemd,nginx,wireguard}` directement comme `jerem` (root-owned + secrets wireguard `chmod 600`). Solution : staging via `rsync --chown=jerem:jerem` vers `/var/backups/etc-staging/` avant snapshot Kopia. Sudoers granulaire pour autoriser uniquement ce rsync sans password.

**Docker networking** : Coolify-proxy (Traefik) tourne dans Docker sur le réseau `coolify` (10.0.1.0/24). Pour qu'il puisse joindre Kopia server bare-metal, on bind sur `0.0.0.0:51515` puis on filtre via UFW : `allow from 10.0.0.0/8 to any port 51515` (même pattern que OpenClaw 18789).

**Mot de passe NAS exposé** : durant le debug, j'ai dû lire `~/.config/rclone/rclone.conf` du Mac de Jerem pour identifier un mismatch avec Doppler. Le mot de passe en clair (`Jerem2026`) a été vu. Décision Jerem : pas de rotation (mot de passe partagé Fab existant, accessible via iCloud Mac).

---

## 📚 Alternatives considérées (historique)

| Option | Rejetée car |
|---|---|
| **Obsidian Sync officiel** | Payant, vendor lock-in, ne couvre pas Notion |
| **Backblaze B2** | Jerem a le NAS ami gratuit, pas besoin de cloud payant |
| **iCloud pour backup serveur** | Pas d'API serveur native, besoin d'un Mac allumé |
| **Restic** (vs Kopia) | Pas d'UI web — Jerem préfère avoir le visuel |
| **Borg** (vs Kopia) | Pas de backend WebDAV natif, moins polyvalent |
| **Notion → incremental seulement** | Aucun outil mature, complexité de code non justifiée vu que Kopia dedupe déjà |

---

## 🔗 Références

- [Kopia documentation](https://kopia.io/docs/)
- [Règle 3-2-1 (US-CERT)](https://www.cisa.gov/sites/default/files/publications/data_backup_options.pdf)
- [SOUL.md section alertes](/_adn/soul.md#2-règles-cardinales-non-negociables)
- Doc config VPS : [[_adn/infra/vps-config-2026-04-17]]

---

*Document vivant, à mettre à jour à chaque évolution de la stratégie.*
