feat(backup-system): cobertura completa del homelab + sync diario a Mega
- backup.sh: fix openclaw (tar-eaba un hostPath stub vacio, 104 bytes/dia desde hace meses); ahora resuelve el PVC real via local-path. - Nuevos servicios: ghost-en/zona-exclusion (VACUUM INTO consistente via node del pod + tar del content), gitea (sqlite .backup + repos + conf, sin packages 6.4G reconstruibles), researchowl (sqlite backup API via python3), roswell-pg, umami-pg, infisical-pg, trilium, vaultwarden, uptime-kuma (sqlite3 .backup), filebrowser-db, home-master (sin ~/.ssh ni caches), k3s token + /etc/rancher/k3s (cadena de restauracion). - Validacion por artefacto: gzip -t + tamano minimo; el job sale 1 si falta algo -> alerta Grafana->Telegram existente. - rclone-mega: de semanal a diario (03:30); --exclude reddit-intel/. - Nuevo CronJob reddit-intel-backup en n97 (unico dato del worker fuera de k3s; su cron local escribia en el mismo disco). - verify.sh: lista completa (20 servicios + reddit-intel), diario, ventana de frescura 3 dias (BusyBox-safe). - backoffLimit 1 (retry recrearia artefactos y romperia la retencion). - RESTORE.md: runbook con la cadena token->state.db->infisical-secrets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
# RESTORE.md — Runbook de restauración del homelab
|
||||
|
||||
> Última revisión: 2026-07-15. Si cambias `backup.sh`, actualiza este runbook.
|
||||
|
||||
## Dónde están las copias
|
||||
|
||||
- **Mega** (`mega:k3s-backups/`): espejo diario de `/data/backups/backups` del master
|
||||
(retención 7 por servicio; 3 para `uptime-kuma` y `home-master`), más el subdir
|
||||
`reddit-intel/` que sube el CronJob de n97.
|
||||
- **Backblaze B2** (`b2:.../`): copia reducida "crown jewels" (últimas 2 de cada
|
||||
servicio irremplazable). Segundo proveedor por si Mega falla.
|
||||
- Acceso: credenciales rclone en Infisical (`/backup-system`, key `RCLONE_CONF`).
|
||||
**Si el cluster está muerto, Infisical también** → usa el login de Mega/B2 de la
|
||||
cuenta personal (Vaultwarden... que también está en el backup: guarda estas dos
|
||||
contraseñas TAMBIÉN en un gestor externo al homelab, p. ej. el llavero del móvil).
|
||||
|
||||
## ⚠️ La cadena de dependencias (leer antes de restaurar nada)
|
||||
|
||||
```
|
||||
token k3s ──> state.db (todos los Secrets k8s) ──> infisical-secrets (ENCRYPTION_KEY)
|
||||
│
|
||||
v
|
||||
dump infisical-pg (secrets de apps)
|
||||
│
|
||||
v
|
||||
resto de apps (n8n, ghost smtp, ddns, runner…)
|
||||
```
|
||||
|
||||
1. `k3s-db_*.tar.gz` contiene `state.db` **+ `/var/lib/rancher/k3s/server/token` +
|
||||
`/etc/rancher/k3s/`**. Sin ese token, un k3s nuevo NO puede leer el state.db
|
||||
(los datos de bootstrap van cifrados con él).
|
||||
2. El Secret `infisical-secrets` (ns `infisical`: `ENCRYPTION_KEY`, `AUTH_SECRET`)
|
||||
está DENTRO de state.db. Sin él, el dump `infisical-pg` es indescifrable.
|
||||
3. n8n es autosuficiente: su `encryptionKey` va dentro de `n8n_*.tar.gz`
|
||||
(`.n8n/config`).
|
||||
|
||||
## Escenario A — Perder un PVC / una app (lo habitual)
|
||||
|
||||
Restaurar solo el artefacto de esa app (ver tabla). El cluster sigue vivo.
|
||||
|
||||
## Escenario B — Muere el disco del master (chemavx-k8)
|
||||
|
||||
1. Reinstalar Ubuntu + k3s server (misma versión mayor; ver skill k8s-infra).
|
||||
2. Restaurar del `k3s-db_*.tar.gz`: `/etc/rancher/k3s/`, el fichero `token` en
|
||||
`/var/lib/rancher/k3s/server/token` y `state.db*` en
|
||||
`/var/lib/rancher/k3s/server/db/` (con k3s parado). Arrancar k3s.
|
||||
3. Con el API server vivo: los manifests los trae ArgoCD desde Gitea → pero Gitea
|
||||
está en el master: restaurar primero Gitea (tabla) para que ArgoCD sincronice.
|
||||
4. Restaurar los datos de apps del master (tabla). Reinstalar Tailscale a mano
|
||||
(está fuera de k8s).
|
||||
|
||||
## Escenario C — Muere el disco del worker (n97)
|
||||
|
||||
Reinstalar Ubuntu + k3s agent (token del master), dejar que los pods se
|
||||
reprogramen y restaurar: ghost-en, zona-exclusion, researchowl, trilium,
|
||||
umami-pg, roswell-pg (la BD corre en n97) y `/opt/reddit-intel` + Docker.
|
||||
|
||||
## Tabla de restauración por artefacto
|
||||
|
||||
| Artefacto | Cómo restaurar |
|
||||
|---|---|
|
||||
| `n8n_*.tar.gz` | Extraer en `/data/n8n` (hostPath master), reiniciar deploy. Incluye encryptionKey. |
|
||||
| `openclaw_*.tar.gz` | Extraer en el PVC `openclaw-pvc` (pod parado). |
|
||||
| `homarr_*.tar.gz` | `db/db.sqlite` → PVC db; `redis/dump.rdb` → PVC redis. |
|
||||
| `authentik-pg_*.sql.gz` | `gunzip -c \| kubectl exec -i postgresql-0 -n authentik -- psql -U authentik authentik` (BD vacía primero). |
|
||||
| `grafana_*.tar.gz` | `grafana.db` → PVC de grafana, reiniciar pod. |
|
||||
| `k3s-db_*.tar.gz` | Ver Escenario B. **No** restaurar sobre un cluster sano. |
|
||||
| `gitea_*.tar.gz` | Extraer en el PVC de gitea; **renombrar `gitea/gitea-backup.db` → `gitea/gitea.db`**. El registry (packages) NO está: re-push de imágenes vía CI (re-run de Gitea Actions por app). |
|
||||
| `ghost-*-db_*.db.gz` | `gunzip` → `content/data/ghost.db` (pod parado). |
|
||||
| `ghost-*-content_*.tar.gz` | Extraer en `/var/lib/ghost/content` (incluye imágenes, temas, `data/redirects.yaml`; el `data/ghost.db` del tar es copia en caliente — usa el `-db_*.db.gz`, que es la consistente). |
|
||||
| `researchowl_*.db.gz` | `gunzip` → `/data/researchowl.db` en el PVC (pod parado; borrar `-wal`/`-shm` viejos). |
|
||||
| `roswell-pg_*.sql.gz` / `umami-pg_*.sql.gz` | Como authentik: `psql` contra el deploy/postgres de su namespace. |
|
||||
| `infisical-pg_*.sql.gz` | **Primero** debe existir el Secret `infisical-secrets` original (viene en state.db; si no, exportarlo ANTES de perderlo). Luego psql contra `infisicalDB` en `postgresql-0`. |
|
||||
| `trilium_*.tar.gz` | Extraer en `/home/node/trilium-data` (PVC, pod parado). |
|
||||
| `vaultwarden_*.tar.gz` | Extraer en el PVC (pod parado). Incluye db, attachments y claves RSA. |
|
||||
| `uptime-kuma_*.db.gz` | `gunzip` → `/app/data/kuma.db` (pod parado, borrar `-wal`/`-shm`). |
|
||||
| `filebrowser-db_*.tar.gz` | Extraer en el PVC `filebrowser-db`. |
|
||||
| `home-master_*.tar.gz` | Extraer en `/home/chemavx`. **No incluye `~/.ssh`** (regenerar claves) ni `~/bin` ni venvs/caches (reinstalar). |
|
||||
| `reddit-intel/reddit-intel_*.tar.gz` | Extraer en `/opt/reddit-intel` de n97, `docker compose up -d`. |
|
||||
|
||||
## Qué NO está en el backup (reconstruible, asumido)
|
||||
|
||||
- Registry de imágenes de Gitea (re-build por CI), modelos Ollama (re-pull),
|
||||
hf-cache de roswell, Prometheus TSDB (histórico de métricas se pierde),
|
||||
registry-cache, SO de los nodos, Tailscale (re-join), certificados TLS
|
||||
(cert-manager los re-emite), DNS (favonia lo reconstruye).
|
||||
- `~/.ssh` del master: excluido a propósito — regenerar par ed25519 y
|
||||
re-autorizar en n97/Gitea.
|
||||
|
||||
## Simulacro
|
||||
|
||||
Tras cualquier cambio gordo en `backup.sh`, repetir el simulacro: bajar de Mega
|
||||
el `ghost-en-db` más reciente, `PRAGMA integrity_check` + `SELECT count(*) FROM posts`,
|
||||
y extraer un `home-master` en /tmp comprobando que `~/.claude/` está dentro.
|
||||
Reference in New Issue
Block a user