Saltar a contenido

Incidente: el T7 (USB) se desconectó y dejó Docker sin arrancar (2026-10-24)

Síntoma Las apps de Docker se cayeron de un momento a otro. Después de reiniciar, el T7 tardaba mucho en aparecer y Docker no arrancaba
Servicio afectado Todo lo que corre en Docker: Gitea, la tienda (k3s), Immich, memos y las sesiones de Claude (tmux-host)
Causa El enlace USB del Samsung T7 se reseteó hasta desconectar el disco con escrituras a medias. El ext4 quedó con directorios corruptos, entre ellos /DATA/t7/docker/runtimes, y dockerd no pudo arrancar
Agravante Todo lo crítico vivía en ese disco USB: el data-root de Docker, Gitea, k3s y la base de Immich
Estado Resuelto. El T7 está reparado, lo crítico pasó al SSD y al HDD, y el T7 quedó sin ahorro de energía USB y vigilado por Telegram

Línea de tiempo (hora local, UTC-5)

Hora Qué pasó
22/10 y 23/10 Ya había 105 y 96 errores USB del T7 por arranque, en ráfagas durante escrituras pesadas. Nadie los vio
10:55:48 usb 2-3: device not accepting address 2, error -62, seguido de resets en cadena
10:56:56 USB disconnect. ext4 pasa a solo lectura en plena escritura (I/O error while writing superblock)
10:57–10:58 El kernel intenta 6 veces reconocerlo y falla siempre (device descriptor read/8, error -110). En otro puerto tampoco responde: el controlador del T7 quedó colgado
11:23 Reinicio del NAS
11:23:59–11:40:48 systemd-fsck pasa 17 minutos recuperando el journal (3,9 millones de archivos). Docker tenía RequiresMountsFor=/DATA/t7, así que todas las apps esperaron
11:48 Aparecen errores de checksum en directorios (redis-server, minio) y ls /DATA/t7 devuelve Bad message
11:49 dockerd cae: failed to start daemon: readdirnames /DATA/t7/docker/runtimes: bad message
12:20–12:53 Corre recuperar-t7.sh: e2fsck, copia de lo crítico al SSD, Docker de vuelta al HDD
13:32 El T7 pasa al puerto USB 3 trasero. Se desenchufó montado, pero el journal se recuperó sin errores
14:00 Se aplica usb3_lpm_permit=0 y se instala el vigilante

Diagnóstico

  1. Era el enlace USB, no el disco. SMART leído con smartctl -d sntasmedia da 0 errores de datos, 3 % de desgaste y 38 °C. Los errores son -62/-110 y resets a nivel de protocolo USB, antes de llegar al sistema de archivos. El disco sí acumula 253 minutos por encima de la temperatura de advertencia y 40.959 horas de uso.
  2. Se veía venir:
    journalctl -k -b -1 | grep -cE "usb 2-3: reset|error -110|error -62"
    
  3. Por qué tarda en aparecer después del reinicio: es el fsck (17 min), no el reconocimiento USB:
    journalctl -b | grep systemd-fsck
    
  4. El daño:
    dumpe2fs -h /dev/disk/by-uuid/1f07679b-7425-4b53-9279-66ddc97599fe | grep -E "state|FS Error count"
    
    Daba clean with errors y el contador llegó a 7.199.

memos no era el disco

memos quedó en un bucle de reinicios con table system_setting already exists. Era una migración fallida por actualización de versión, no la corrupción. Al volver Docker al HDD, volvió con su contenedor de imagen estable 0.28 y anduvo solo.

Recuperación

scripts/nas-arranque/recuperar-t7.sh se corre como servicio de systemd, así que sobrevive aunque se caigan la terminal y la sesión de Claude:

sudo systemd-run --unit=recuperar-t7 --collect --setenv=CONFIRMO=si \
  bash /DATA/AppData/k3d-lab-v3/recuperacion-t7/recuperar-t7.sh
sudo tail -f /media/HDD-Storage/recuperacion-t7/recuperar.log
  1. Para Docker y todos los contenedores. Usa mask --runtime para que ningún timer lo levante a mitad de la reparación.
  2. Desmonta el T7 (con umount -R, porque quedan overlays de Docker) y corre e2fsck -fyD -z <undo>. El archivo undo permite revertir con e2undo.
  3. Lo vuelve a montar, en solo lectura si e2fsck no pudo corregir todo.
  4. Copia appdata/{gitea,immich,k3d-lab} al SSD (/DATA/ssd-appdata). Si rsync devuelve algo distinto de 0 o 24, aborta.
  5. Reapunta los symlinks de /media/HDD-Storage/AppData. Si encuentra una carpeta real, la aparta y verifica cada enlace.
  6. Vuelve el data-root de Docker a /media/HDD-Storage/docker. Verifica el cambio y restaura el archivo si no se aplicó.
  7. Quita los drop-ins RequiresMountsFor=/DATA/t7.
  8. Arranca Docker y los contenedores.

Antes de correrlo, feature-dev:code-reviewer lo revisó y encontró 8 problemas que se corrigieron. El más grave era que fuser -km sobre un T7 desmontado habría matado procesos de todo /DATA. Además se probó por separado el cambio de daemon.json (7 formatos de espaciado) y el reapuntado de enlaces (incluida una carpeta real con datos).

Resultado:

  • e2fsck terminó con código 1 (corregido).
  • lost+found recibió 264 entradas (14 GB), todas restos de capas de contenedores.
  • Las 3 copias quedaron idénticas en cantidad de archivos.
  • 29 contenedores arriba y 26 pods Running.
  • gitea.db pasó integrity_check, con los datos del día.
  • Immich, la tienda y Docs andan.

Cambios para que no se repita

Cambio Dónde
Lo crítico fuera del USB: Gitea, k3s y la base de Immich en el SSD; el data-root de Docker en el HDD symlinks /media/HDD-Storage/AppData/* → /DATA/ssd-appdata, /etc/docker/daemon.json
Docker no depende del T7: si el T7 no está, solo faltan las miniaturas de Immich y los caches del CI drop-ins 10-t7.conf → .disabled-*
Sin LPM USB 3 en el puerto del T7, al arrancar y en cada conexión t7-usb-quirks.service, 99-t7-usb.rules
Aviso por Telegram de resets USB, desconexiones o errores ext4 (cada 5 min) vigilar-usb-t7.timer
T7 en el puerto USB 3 trasero, directo a la placa físico

Por qué usb3_lpm_permit y no otra cosa

  • power/usb3_hardware_lpm_u1/u2 del dispositivo son de solo lectura.
  • El quirk 04e8:4001:k de usbcore (sin LPM) solo se aplica al enumerar el dispositivo, y authorized 0/1 no vuelve a leer los quirks (probado: el LPM seguía enabled). usbcore está integrado al kernel y la línea de arranque de ZimaOS se reescribe en cada actualización.
  • echo 0 > /sys/bus/usb/devices/<T7>/port/usb3_lpm_permit aplica en caliente, con el disco montado. Verificado: u1/u2 pasan de enabled a disabled, y también al disparar la regla udev con un evento sintético.
d=/sys/bus/usb/devices/2-3   # el puerto puede cambiar
cat $d/port/usb3_lpm_permit $d/power/usb3_hardware_lpm_u1 $d/power/usb3_hardware_lpm_u2

Si los resets vuelven aun sin LPM, el siguiente paso es desactivar UAS para el T7 (usb-storage.quirks=04e8:4001:u). Es más estable en algunos casos, pero más lento.

Operación del T7 desde ahora

  • Antes de desenchufarlo: parar immich-server y gitea-runner y correr umount /DATA/t7.
  • Al conectarlo: ZimaOS lo monta solo en /media/T7, no en /DATA/t7. Hay que correr mount /DATA/t7 y reiniciar immich-server y gitea-runner, porque sus montajes no se actualizan solos.
  • tmux-host monta /DATA como rprivate, así que no ve los remontajes del T7 hasta que se recrea. A las sesiones de Claude no les afecta.

Lecciones

  1. Un disco USB no es almacenamiento principal. Puede fallar el disco, pero también el cable, el puerto, la energía o la temperatura. Lo que no se puede caer va en discos internos; en el USB, solo lo regenerable.
  2. Los avisos ya estaban en el log desde dos días antes. Faltaba quien los leyera: ahora los lee vigilar-usb-t7.
  3. Una dependencia de arranque es un punto único de falla. RequiresMountsFor evitaba arrancar con carpetas vacías, pero ataba todo Docker a un disco USB.
  4. Un script de reparación se prueba antes de correrlo. Hay que revisarlo y probar por separado las partes que cambian configuración, y sobre todo que aborte en lugar de seguir con datos a medias.