agenthub/docs/RUNBOOK-restore.md
Paperclip FoundingEngineer bdd5d92ba7 Initial AgentHub codebase for Coolify deployment
Complete implementation ready for Coolify:
- Node.js 22 + Fastify + socket.io backend
- PostgreSQL 16 + Redis 7 services
- Docker Compose configuration
- Deployment scripts and documentation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-01 21:25:57 +00:00

377 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AgentHub Backup & Restore Runbook
**Version**: 1.0
**Date**: 2026-04-30
**Maintainer**: FoundingEngineer
**Related ADR**: [ADR-0004 Déploiement](./adr/0004-deploiement-phase1-lan-phase2-coolify.md)
---
## Table of Contents
1. [Backup Strategy](#backup-strategy)
2. [Automated Backups](#automated-backups)
3. [Manual Backup](#manual-backup)
4. [Restore Procedure](#restore-procedure)
5. [Disaster Recovery](#disaster-recovery)
6. [Weekly Encrypted Backups](#weekly-encrypted-backups)
7. [Troubleshooting](#troubleshooting)
---
## Backup Strategy
AgentHub utilise une stratégie de backup à deux niveaux :
1. **Backups locaux journaliers** : pg_dump à 03:00 UTC, rétention 14 jours sur `/opt/agenthub/backups`
2. **Backups distants hebdomadaires** : copie chiffrée GPG vers Scaleway Object Storage Paris, rétention 8 semaines
**RPO (Recovery Point Objective)** : 24 heures
**RTO (Recovery Time Objective)** : < 30 minutes pour un restore standard
---
## Automated Backups
Les backups sont orchestrés par **Ofelia** (scheduler Docker) qui exécute le container `backup` quotidiennement.
### Vérifier le statut des backups
```bash
# Lister les backups locaux
ls -lh /opt/agenthub/backups/
# Vérifier les logs Ofelia
docker compose -f compose.lan.yml logs ofelia
# Vérifier les logs du dernier backup
docker compose -f compose.lan.yml logs backup | tail -50
```
### Configuration du backup automatique
Le service `backup` est configuré dans `compose.lan.yml` et `compose.coolify.yml` :
- **Schedule** : `0 0 3 * * *` (03:00 UTC tous les jours)
- **Rétention locale** : 14 jours (gérée automatiquement par le script)
- **Format** : PostgreSQL custom format (`-Fc`), optimal pour restore sélectif
- **Destination** : `/opt/agenthub/backups/agenthub_YYYYMMDD_HHMMSS.dump`
---
## Manual Backup
### Backup manuel immédiat
```bash
# Exécuter un backup manuel
docker compose -f compose.lan.yml run --rm backup
# Vérifier que le backup est créé
ls -lh /opt/agenthub/backups/ | tail -1
```
### Backup one-shot hors Docker
```bash
# Depuis l'hôte (nécessite psql installé)
PGPASSWORD='<postgres-password>' pg_dump -Fc \
-h localhost \
-p 5432 \
-U agenthub \
-d agenthub \
-f "/opt/agenthub/backups/manual_$(date -u +%Y%m%d_%H%M%S).dump"
```
---
## Restore Procedure
### Pré-requis
- Fichier de backup disponible (`.dump`)
- Accès au serveur Postgres cible
- Variables d'environnement Postgres configurées (`PGHOST`, `PGUSER`, `PGPASSWORD`)
### Restore standard (production down)
**Cas d'usage** : restaurer la base de production après corruption ou perte de données.
```bash
# 1. Arrêter l'application pour éviter les connexions actives
docker compose -f compose.lan.yml stop app
# 2. Lancer le restore (interactive confirmation)
docker compose -f compose.lan.yml run --rm \
-e PGHOST=postgres \
-e PGDATABASE=agenthub \
-e PGUSER=agenthub \
-e PGPASSWORD="${POSTGRES_PASSWORD}" \
backup \
/usr/local/bin/restore.sh /backups/agenthub_20260430_030000.dump
# 3. Vérifier l'intégrité après restore
docker compose -f compose.lan.yml run --rm postgres \
psql -h postgres -U agenthub -d agenthub -c "SELECT COUNT(*) FROM agents;"
# 4. Redémarrer l'application
docker compose -f compose.lan.yml start app
```
### Restore vers base éphémère (test)
**Cas d'usage** : valider un backup avant de l'utiliser en production.
```bash
# 1. Créer une base de test
docker compose -f compose.lan.yml exec postgres \
psql -U agenthub -d postgres -c "CREATE DATABASE agenthub_test OWNER agenthub;"
# 2. Restore vers la base de test
docker compose -f compose.lan.yml run --rm \
-e PGHOST=postgres \
-e PGDATABASE=agenthub_test \
-e PGUSER=agenthub \
-e PGPASSWORD="${POSTGRES_PASSWORD}" \
-e SKIP_CONFIRMATION=yes \
backup \
/usr/local/bin/restore.sh /backups/agenthub_20260430_030000.dump agenthub_test
# 3. Vérifier la restauration
docker compose -f compose.lan.yml exec postgres \
psql -U agenthub -d agenthub_test -c "\dt"
# 4. Comparer le nombre de tables
PROD_TABLES=$(docker compose -f compose.lan.yml exec postgres \
psql -U agenthub -d agenthub -t -c \
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';")
TEST_TABLES=$(docker compose -f compose.lan.yml exec postgres \
psql -U agenthub -d agenthub_test -t -c \
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';")
echo "Prod tables: ${PROD_TABLES}"
echo "Test tables: ${TEST_TABLES}"
# 5. Cleanup
docker compose -f compose.lan.yml exec postgres \
psql -U agenthub -d postgres -c "DROP DATABASE agenthub_test;"
```
---
## Disaster Recovery
### Scénario 1 : Corruption de la base de production
**Impact** : Application down, données corrompues
1. Arrêter l'application : `docker compose -f compose.lan.yml stop app`
2. Identifier le dernier backup sain : `ls -lht /opt/agenthub/backups/ | head -5`
3. Restaurer depuis ce backup (voir [Restore standard](#restore-standard-production-down))
4. Redémarrer : `docker compose -f compose.lan.yml start app`
5. Smoke test : `curl -fsS http://<host>:3000/healthz`
**Temps estimé** : 510 minutes
---
### Scénario 2 : Perte totale du volume pgdata
**Impact** : Volume Docker perdu, base de données inexistante
1. Recréer le volume : `docker volume create <project>_pgdata`
2. Démarrer Postgres : `docker compose -f compose.lan.yml up -d postgres`
3. Attendre que Postgres initialise : `docker compose -f compose.lan.yml logs -f postgres`
4. Restaurer le dernier backup (voir [Restore standard](#restore-standard-production-down))
5. Redémarrer l'application : `docker compose -f compose.lan.yml up -d`
**Temps estimé** : 1015 minutes
---
### Scénario 3 : Perte totale du serveur
**Impact** : Serveur hôte down, backups locaux inaccessibles
1. Provisionner un nouveau serveur (voir `scripts/bootstrap.sh` dans le repo)
2. Télécharger le backup hebdomadaire chiffré depuis Scaleway Object Storage :
```bash
aws s3 cp \
"s3://${S3_BUCKET}/weekly/agenthub_<date>.dump.gpg" \
/tmp/backup.dump.gpg \
--endpoint-url "${S3_ENDPOINT}"
```
3. Déchiffrer le backup :
```bash
gpg --decrypt /tmp/backup.dump.gpg > /tmp/backup.dump
```
4. Copier le backup dans le volume :
```bash
mkdir -p /opt/agenthub/backups
mv /tmp/backup.dump /opt/agenthub/backups/
```
5. Lancer la stack : `docker compose -f compose.lan.yml up -d`
6. Restaurer (voir [Restore standard](#restore-standard-production-down))
**Temps estimé** : 3060 minutes (selon taille du backup et bande passante)
---
## Weekly Encrypted Backups
Les backups hebdomadaires (dimanche 03:00 UTC) sont chiffrés GPG et uploadés vers Scaleway Object Storage.
### Vérifier les backups distants
```bash
# Lister les backups sur Scaleway
aws s3 ls "s3://${S3_BUCKET}/weekly/" --endpoint-url "${S3_ENDPOINT}"
```
### Télécharger et déchiffrer un backup distant
```bash
# Télécharger
aws s3 cp \
"s3://${S3_BUCKET}/weekly/agenthub_20260427_030000.dump.gpg" \
/tmp/backup.dump.gpg \
--endpoint-url "${S3_ENDPOINT}"
# Déchiffrer (nécessite la clé privée GPG correspondante)
gpg --decrypt /tmp/backup.dump.gpg > /tmp/backup.dump
# Restaurer
# (voir section Restore Procedure)
```
### Configuration S3 et GPG
Les variables d'environnement suivantes doivent être configurées dans `.env` :
```bash
S3_ENDPOINT=https://s3.fr-par.scw.cloud
S3_BUCKET=agenthub-backups-paris
AWS_ACCESS_KEY_ID=<scaleway-access-key>
AWS_SECRET_ACCESS_KEY=<scaleway-secret-key>
GPG_RECIPIENT_KEY=<gpg-public-key-id>
```
---
## Troubleshooting
### Le backup automatique ne s'exécute pas
**Symptômes** : Aucun nouveau backup depuis > 24h
1. Vérifier que le service Ofelia est up :
```bash
docker compose -f compose.lan.yml ps ofelia
```
2. Vérifier les logs Ofelia :
```bash
docker compose -f compose.lan.yml logs ofelia | grep backup-daily
```
3. Vérifier les labels du service `backup` dans `compose.lan.yml` :
```yaml
labels:
ofelia.enabled: "true"
ofelia.job-exec.backup-daily.schedule: "0 0 3 * * *"
ofelia.job-exec.backup-daily.command: "/usr/local/bin/backup.sh"
```
4. Relancer Ofelia :
```bash
docker compose -f compose.lan.yml restart ofelia
```
---
### Le restore échoue avec "permission denied"
**Cause probable** : Mauvais utilisateur Postgres ou base verrouillée
1. Vérifier que l'application est arrêtée :
```bash
docker compose -f compose.lan.yml stop app
```
2. Tuer les connexions actives :
```bash
docker compose -f compose.lan.yml exec postgres \
psql -U postgres -d postgres -c \
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='agenthub';"
```
3. Relancer le restore
---
### Backup file is empty or missing
**Cause probable** : pg_dump a échoué (credentials, réseau, espace disque)
1. Vérifier les logs du container backup :
```bash
docker compose -f compose.lan.yml logs backup
```
2. Vérifier l'espace disque sur l'hôte :
```bash
df -h /opt/agenthub/backups
```
3. Tester manuellement pg_dump :
```bash
docker compose -f compose.lan.yml run --rm backup \
pg_dump -h postgres -U agenthub -d agenthub --version
```
---
### GPG encryption fails lors du backup hebdomadaire
**Cause probable** : `GPG_RECIPIENT_KEY` manquant ou invalide
1. Vérifier que la clé GPG est importée dans le container :
```bash
docker compose -f compose.lan.yml run --rm backup gpg --list-keys
```
2. Importer la clé publique si manquante :
```bash
docker compose -f compose.lan.yml run --rm backup \
gpg --import /path/to/public-key.asc
```
3. Vérifier la variable d'environnement `GPG_RECIPIENT_KEY` dans `.env`
---
## Drill de restore (procédure mensuelle recommandée)
**Objectif** : Valider que les backups sont restaurables et complets.
```bash
# 1. Sélectionner un backup récent
BACKUP_FILE="/opt/agenthub/backups/$(ls -t /opt/agenthub/backups/ | head -1)"
echo "Testing backup: ${BACKUP_FILE}"
# 2. Restore vers base éphémère
# (voir section "Restore vers base éphémère (test)")
# 3. Vérifier le checksum du backup
sha256sum "${BACKUP_FILE}" > /tmp/backup-checksum.txt
cat /tmp/backup-checksum.txt
# 4. Comparer le nombre de tables
# (voir section "Restore vers base éphémère (test)")
# 5. Cleanup et documenter
# Enregistrer le résultat du drill dans un log de suivi
```
**Fréquence recommandée** : 1 fois par mois minimum
---
## Contacts et Escalation
- **Responsable technique** : FoundingEngineer
- **Documentation source** : `agenthub/docs/RUNBOOK-restore.md`
- **Scripts** : `agenthub/scripts/{backup,restore}.sh`
- **ADR associé** : [ADR-0004](./adr/0004-deploiement-phase1-lan-phase2-coolify.md)