From 60d8ca9bbf1087d7b22f23d2432d61b1b65999e8 Mon Sep 17 00:00:00 2001 From: sergio Date: Fri, 7 Aug 2026 08:10:30 -0400 Subject: [PATCH] =?UTF-8?q?respaldo:=20Storage=20Box=20BX11=20en=20Hetzner?= =?UTF-8?q?=20=E2=80=94=20no=20dependemos=20m=C3=A1s=20de=20un=20laptop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El usuario pidió respaldar en «el volumen, aunque sea montándolo aquí». No se puede: un HC Volume es un dispositivo de bloque por RED, sólo se adjunta a servidores de Hetzner del mismo DC, y sólo a uno a la vez. harkaq-cosecha es el disco del worker efímero y vvv está al 78%. El producto correcto es un Storage Box: se monta desde el laptop (SSHFS/CIFS) y habla rsync/borg/restic por SSH. BX11 = 1 TiB, €3,20/mes, sin alta. Creado en hel1 con protección de borrado y la clave github5. rsync plano y no borg: el store es CAS, los ficheros son inmutables y se nombran por hash, así que incremental es exactamente lo correcto y no hay repo ni claves que mantener. borg comprimiría 4x (79% del store es .debug_) pero 126 G en 1 TiB no aprieta. El store va SIN --delete a propósito: un respaldo que replica los borrados no protege del borrado por error, y store-gc.sh acaba de demostrar que puede equivocarse en silencio. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/respaldo-storagebox.sh | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100755 scripts/respaldo-storagebox.sh diff --git a/scripts/respaldo-storagebox.sh b/scripts/respaldo-storagebox.sh new file mode 100755 index 00000000..02c06d6e --- /dev/null +++ b/scripts/respaldo-storagebox.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Respaldo de hammer al Storage Box de Hetzner (BX11, 1 TiB, hel1). +# +# ── POR QUÉ UN STORAGE BOX Y NO EL VOLUMEN ────────────────────────────────────────────────────── +# El usuario pidió respaldar «en el volumen, aunque sea montándolo aquí». **No se puede**: un Hetzner +# Cloud Volume es un dispositivo de bloque por RED que sólo se adjunta a servidores de Hetzner Cloud +# del mismo datacenter — no se monta en un laptop, y encima sólo puede estar en UN servidor a la vez. +# `harkaq-cosecha` (100 G) es el disco del worker efímero y `vvv` (260 G, en gioser) está al 78%. +# El Storage Box SÍ es el producto para esto: se monta desde acá (SSHFS/CIFS) y habla rsync/borg/ +# restic por SSH. BX11 = 1 TiB por €3,20/mes, sin coste de alta. Ver [[capacidad-disco-proyeccion]]. +# +# ── POR QUÉ rsync PLANO Y NO borg/restic ──────────────────────────────────────────────────────── +# El store es CAS: los ficheros son INMUTABLES y se nombran por hash. Nunca cambian, sólo aparecen o +# desaparecen. Para eso rsync incremental es exactamente lo correcto y no hay que mantener repos ni +# claves de cifrado. borg comprimiría 4x (el 79% del store es `.debug_`), pero 126 G en 1 TiB no +# aprieta a nadie: no vale la complejidad todavía. Si algún día aprieta, ahí sí borg. +# +# ── EL STORE NO SE BORRA EN EL DESTINO, A PROPÓSITO ───────────────────────────────────────────── +# `--delete` NO va sobre el store. Un respaldo que replica los borrados no protege del borrado por +# error, y el 2026-08-07 `store-gc.sh` demostró que puede equivocarse EN SILENCIO (reportó 364 +# artefactos borrados sin haber borrado ninguno). El respaldo acumula; podarlo es una decisión +# deliberada y aparte. El repo sí va con `--delete`, que para eso está git. +# +# Uso: scripts/respaldo-storagebox.sh [--seco] +set -eu + +SB_USER="${SB_USER:-u647150}" +SB_HOST="${SB_HOST:-u647150.your-storagebox.de}" +SB_PORT="${SB_PORT:-23}" # 23 = SSH completo (rsync). El 22 sólo da SFTP/SCP restringido. +KEY="${KEY:-$HOME/.ssh/github5}" +RAIZ="${RAIZ:-/home/sergio/hammer}" + +SECO="" +[ "${1:-}" = "--seco" ] && SECO="--dry-run" + +SSH_CMD="ssh -p $SB_PORT -i $KEY -o StrictHostKeyChecking=accept-new" + +if ! $SSH_CMD "$SB_USER@$SB_HOST" true 2>/dev/null; then + echo "!! No hay SSH al Storage Box ($SB_HOST:$SB_PORT)." + echo "!! Si acabás de crearlo, el DNS tarda unos minutos en propagar. Reintentá." + exit 1 +fi + +echo "==> destino: $SB_USER@$SB_HOST:$SB_PORT ${SECO:+(SECO)}" +$SSH_CMD "$SB_USER@$SB_HOST" 'mkdir -p hammer/store hammer/repo hammer/estado' 2>/dev/null || true + +# 1. EL STORE — lo caro e irreemplazable (meses de CPU). Sin --delete, ver cabecera. +echo "==> store" +rsync -a --info=progress2 --partial $SECO -e "$SSH_CMD" \ + "$RAIZ/store/" "$SB_USER@$SB_HOST:hammer/store/" + +# 2. EL REPO — barato, y ya está espejado a GitHub privado; va igual para tener todo en un sitio. +# Se excluye lo regenerable y lo gigante: store (arriba), work, target, .dev-fs. +echo "==> repo" +rsync -az --delete $SECO -e "$SSH_CMD" \ + --exclude /store --exclude /work --exclude /target --exclude /.dev-fs \ + --exclude /dist --exclude /.scratch \ + "$RAIZ/" "$SB_USER@$SB_HOST:hammer/repo/" + +# 3. El grafo de estado, aparte, para poder mirar qué había sin desempacar nada. +echo "==> estado" +rsync -az $SECO -e "$SSH_CMD" "$RAIZ/docs/state/" "$SB_USER@$SB_HOST:hammer/estado/" 2>/dev/null || true + +echo "==> ocupación en el Storage Box:" +$SSH_CMD "$SB_USER@$SB_HOST" 'df -h . 2>/dev/null | tail -1; du -sh hammer/* 2>/dev/null'