- 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>
5.7 KiB
5.7 KiB
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/backupsdel master (retención 7 por servicio; 3 parauptime-kumayhome-master), más el subdirreddit-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, keyRCLONE_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…)
k3s-db_*.tar.gzcontienestate.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).- El Secret
infisical-secrets(nsinfisical:ENCRYPTION_KEY,AUTH_SECRET) está DENTRO de state.db. Sin él, el dumpinfisical-pges indescifrable. - n8n es autosuficiente: su
encryptionKeyva dentro den8n_*.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)
- Reinstalar Ubuntu + k3s server (misma versión mayor; ver skill k8s-infra).
- Restaurar del
k3s-db_*.tar.gz:/etc/rancher/k3s/, el ficherotokenen/var/lib/rancher/k3s/server/tokenystate.db*en/var/lib/rancher/k3s/server/db/(con k3s parado). Arrancar k3s. - 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.
- 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).
~/.sshdel 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.