Files
k8s-manifests/CLAUDE.md
T

5.5 KiB

Notas para agentes que trabajan en este cluster

Este es el directorio de trabajo de Hermes (el agente de Telegram) y de cualquier sesión de Claude Code sobre el homelab. Aquí van solo las cosas que engañan al mirarlas: lo que parece una avería y no lo es, y lo que parece inofensivo y no lo es. Lo demás está en el skill k8s-infra.

n8n: las ejecuciones «running» que nunca terminan NO están colgadas

Desde el 2026-07-30 el deployment lleva EXECUTIONS_DATA_SAVE_ON_SUCCESS=none. Cuando un workflow sale bien, n8n crea igualmente la fila en execution_entity, le pone deletedAt al instante y no actualiza el estado, porque la va a tirar. El barrido duro se las lleva cada ~15 min.

O sea que SELECT ... FROM execution_entity enseña filas con status='running' y stoppedAt=NULL que son ejecuciones exitosas ya borradas. No hay nada que arreglar. Ya provocó un diagnóstico en falso el mismo día que se puso.

Para saber si un workflow corre de verdad, mirar workflow_statistics:

SELECT name, count FROM workflow_statistics WHERE workflowId='<id>';
-- production_success sube en cada vuelta aunque la ejecución no se guarde

Sale también en deletedAt IS NOT NULL. Y ojo: startedAt y deletedAt no llevan el mismo huso, así que restarlas no significa nada.

n8n: lo que se ejecuta es la versión PUBLICADA, no el borrador

workflow_entity.nodes es el borrador. Producción usa la versión de workflow_history apuntada por workflow_entity.activeVersionId. Editar nodes a mano y reiniciar no cambia nada y no da ningún error. La API pública (PUT /api/v1/workflows/{id}) sí publica; parcheando SQLite hay que escribir en las dos tablas.

Diagnóstico rápido: comparar las dos columnas. Si difieren, lo que corre no es lo que estás leyendo.

Y aun escribiendo las dos, hay que REINICIAR el pod. n8n carga los workflows activos en memoria al arrancar; un UPDATE en SQLite no se los recarga, y la ejecución siguiente sigue usando la versión vieja sin dar ningún error. Pasó el 2026-07-31: la primera publicación coló solo porque ArgoCD reinició el pod justo después por otro motivo, y la segunda se probó contra el código viejo durante media hora. Regla: kubectl -n n8n rollout restart deploy/n8n y comprobar que el startTime del pod es POSTERIOR a la escritura, antes de dar por buena ninguna prueba.

Antes de tocar una BD de un pod: desarmar ArgoCD

Casi todo está bajo Applications con automated{selfHeal,prune}. Un kubectl scale --replicas=0 para hacer un VACUUM se revierte solo a mitad.

kubectl -n argocd patch app <app> --type json \
  -p '[{"op":"remove","path":"/spec/syncPolicy/automated"}]'
# ... trabajo ...
kubectl -n argocd patch app <app> --type merge \
  -p '{"spec":{"syncPolicy":{"automated":{"prune":true,"selfHeal":true}}}}'

Rearmarlo al final levanta el pod y aplica la config nueva de una vez.

ArgoCD: una Application de directorio se traga los .json

path: n8n incluye *.yaml, *.yml y *.json como manifiestos. Un JSON que no sea un objeto de Kubernetes deja la app en ComparisonError («Object 'Kind' is missing»). Por eso application-n8n.yaml tiene directory.exclude: "workflow-*.json". Los .md no se parsean: este fichero es seguro.

El auto-reinicio de Uptime Kuma sí funciona (desde 2026-07-30) y va cerrado

Kuma cae → notificación «n8n Auto-Restart» → webhook público de n8n → PATCH del restartedAt. Antes no reiniciaba nada (sin RBAC: 403), y sus «éxitos» eran el aviso de FALLO enviándose bien.

  • La SA es n8n-restarter. Desde el 2026-07-31 son Roles por namespace con resourceNames sobre los 12 workloads exactos del SERVICE_MAP (ver n8n/rbac-restarter.yaml), no un ClusterRole abierto. Si añades un servicio al mapa, hay que añadirlo también ahí o dará 403 — deliberado: el mapa y el permiso se mueven juntos. Motivo: esa SA va montada en el pod entero de n8n y los nodos Code leen el token de /var/run/secrets/…, así que cualquier workflow con un nodo Code hereda lo que se conceda aquí. El límite no es el mapa, es el RBAC.
  • La clave del mapa es el nombre exacto del monitor en Kuma. Renombrarlo allí lo desconecta en silencio.
  • El webhook exige la cabecera X-Kuma-Token (secret n8n-kuma-webhook, fuera de git) y desde el 2026-07-31 falla en CERRADO: el env ya no lleva optional, así que sin secreto el pod no arranca, y la puerta lanza error si la variable falta. Antes se saltaba la comprobación «para no quedarte sin avisos», y ese argumento era falso: el aviso de caída lo manda Kuma por su notificación 1, directa, no por este webhook.
  • Para apps bajo ArgoCD, el restartedAt se revierte tras el reinicio. El pod sí se reinicia; la anotación no queda. No es un fallo. Por eso el éxito se comprueba con observedGeneration >= la generation que devolvió el PATCH, y con >= y no con ==: la reversión vuelve a subir la generation.
  • Cómo saber por qué rama fue una ejecución cuando SAVE_ON_SUCCESS=none ya la ha vaciado: por la duración. deletedAt - startedAt (con el offset fijo de 1 h) da ~0,02 s si la puerta rechazó, ~0,3 s si el PATCH falló y se fue por la rama de fallo, y ~60 s si hizo el ciclo completo con su espera.

Un workflow que termina en verde no dice que haya hecho su trabajo

Vale para n8n y para cualquier automatismo de aquí: comprobar el efecto (que el restartedAt cambió, que el pod es nuevo, que el fichero está), no el código de salida ni el contador de éxitos.