Ir al contenido

Operación self-hosted

En una instalación propia, la persona que opera Thulu actualiza la versión, decide cuándo volver atrás, saca backups y limpia lo que queda en el host. Thulu nunca cambia su propia versión: la pantalla “Actualizaciones” informa qué hay y da el comando, y el cambio se corre a mano con la CLI thulu en la máquina que administra la instalación.

Todo lo de esta página aplica sólo a instalaciones self-hosted. En la SaaS lo hace el equipo de Thulu.

  1. Fijate si hay una versión nueva. Cuando hay una, el pie de la barra lateral muestra un punto azul al lado de la versión. Hacé clic ahí, o andá a “Configuración” → “Actualizaciones”.

    Pie de la barra lateral con 'Thulu 0.3.1' y un punto azul
    El punto azul sólo lo ve el operador: indica que hay una versión más nueva en los canales permitidos.
  2. Leé el panel “Versión” y la lista de “Versiones disponibles”. Cada versión dice cuántas migraciones trae, desde qué versiones se puede saltar, si volver atrás necesita restaurar la base y qué variables nuevas exige. “Buscar ahora” fuerza una búsqueda; si no, Thulu busca cada 12 horas.

    Pantalla Actualizaciones con la versión 0.3.1 corriendo y la 0.3.2 disponible, su resumen, notas y el comando thulu update -version 0.3.2
    0.3.2 no trae migraciones y volver a 0.3.1 no requiere restaurar la base. El comando está listo para copiar.
  3. Descargá el paquete de cambios con “Descargar paquete de cambios” (Markdown) o “JSON”. Es el documento para revisar o adjuntar al ticket de cambio: versiones, cada imagen por digest, migraciones, variables obligatorias, pasos manuales, el plan de ejecución y cómo volver. La descarga queda en el registro de auditoría.

  4. Copiá el comando con “Copiar” y abrí una terminal en el host de la instalación, en el directorio donde está deploy/.

  5. Si querés, generá el paquete desde la terminal para ver además qué archivos de deploy/ cambian (la UI no tiene acceso al disco para comparar eso):

    Ventana de terminal
    thulu update -check -version 0.3.2 -o change-0.3.2.md
  6. Corré la actualización y confirmá cuando pregunte “¿Actualizar ahora?”:

    Ventana de terminal
    thulu update -version 0.3.2 -api-key-file ~/.thulu-operator-key

    Con -api-key-file (o OCTOPUS_API_KEY definida) el resultado llega al historial. -operator cambia el nombre que aparece en la columna “Por”. Si la versión trae pasos manuales, la CLI pide que los confirmes con -ack-manual-steps. Con una base de datos externa, a la que la CLI no llega, pide -backup-confirmed para dejar constancia de que sacaste tu propio backup.

  7. Volvé a “Actualizaciones” y mirá el “Historial”. Cada corrida aparece con su resultado y quién la corrió; el arranque del daemon en la versión nueva aparece aparte.

    Historial con corridas de actualización ok y falló, y los arranques del daemon en cada versión
    La primera corrida del 29 de octubre falló porque alguien había editado deploy/docker-compose.yml a mano. La siguiente, nueve minutos después, salió bien.

El comando escribe la versión destino y corre thulu apply, que sigue este orden. Si algo falla antes del paso 5, no cambió nada; si falla después, vuelve solo a la versión anterior.

Paso Qué hace
1. Verifica Baja el manifiesto de la release y verifica su firma con la clave embebida en el binario. Cada imagen y el chart están nombrados por digest, y se verifica la firma de cada uno antes de bajarlo.
2. Usa la CLI de la versión destino La CLI de la versión X aplica la versión X: la baja, compara su sha256 con el manifiesto y le pasa el control.
3. Preflight El salto está permitido, están todas las variables obligatorias, los pasos manuales están confirmados, nadie editó a mano un archivo de deploy/, hay espacio en disco y no hay otro update corriendo.
4. Backup Con el Postgres incluido, un snapshot (pg_dump más /data) en backups/, asociado a la versión que dejás.
5. Cambia Baja las imágenes por digest, reescribe los archivos de deploy/, escribe el bloque administrado de deploy/.env y recrea el stack. Tu docker-compose.override.yml se aplica siempre encima.
6. Confirma Espera /health/ready y comprueba que /info informe la versión nueva. Arrancar con la imagen vieja cuenta como falla.
7. Informa Escribe deploy/.octopus-update.json y manda el resultado al registro de auditoría. Si el envío falla es una advertencia, no un update fallido.

Se puede saltar desde las dos últimas versiones menores. Una instalación más vieja pasa primero por la menor más vieja soportada; apply rechaza el salto si la release no lo permite. Contá con un corte de alrededor de un minuto mientras se recrea el contenedor del daemon.

Una instalación Docker conserva las imágenes y el snapshot de las últimas 3 versiones, contando la que corre (OCTOPUS_KEEP_VERSIONS), así que volver no necesita el registry. “Volver atrás” las lista con el comando exacto de cada una.

Tabla Volver atrás con 0.3.1 en marcha, 0.3.0 sin restaurar la base y 0.2.1 con -restore y el nombre del snapshot
Volver a 0.3.0 sólo cambia imágenes. Volver a 0.2.1 restaura el snapshot del 15 de octubre, y la fila dice desde qué fecha se pierden datos.
  1. En “Configuración” → “Actualizaciones” → “Volver atrás”, buscá la versión y apretá “Copiar”.
  2. En la terminal del host, corré el comando. Hay dos casos:
    • thulu update -to 0.3.0: la versión es el rollback_to de la que corre. Las migraciones de la versión actual sólo agregan cosas que esa versión sabe leer, así que cambian las imágenes y la base queda como está.
    • thulu update -to 0.2.1 -restore octopus_20261005_141610.sql: cualquier otra versión guardada. La base vuelve al snapshot tomado al dejar esa versión, y se pierde todo lo escrito desde entonces.
  3. Confirmá cuando pregunte “¿Volver ahora?”. La terminal muestra antes qué imágenes cambian y qué pasa con la base.

Desde la terminal, thulu update -rollback vuelve a la versión anterior, thulu update -kept lista las versiones guardadas con su comando, y thulu update -keep 0.3.0 fija una para que nunca se borre (-unkeep la libera). Con una base externa, la restaurás vos y pasás -restore external.

En Kubernetes el proceso es el mismo comando, corrido desde una máquina con helm, acceso al cluster y el directorio de la instalación que dejó thulu init:

Ventana de terminal
thulu update -check -version 0.3.2 -o change-0.3.2.md
thulu update -version 0.3.2

La CLI verifica la firma del chart y de cada imagen, y corre helm upgrade --install octopus <chart> --namespace octopus --wait --atomic --timeout 10m con cada imagen fijada por digest. Los pods viejos siguen atendiendo hasta que los nuevos están listos, y si nunca llegan, Helm revierte solo. Para volver, thulu update -to <versión> usa el historial de Helm; en Kubernetes “Volver atrás” no lista versiones y la versión a la que volver se saca del “Historial”.

En una máquina que llega a GitHub y al registry, thulu mirror 0.3.2 -to registry.empresa.com/octopus -dir /srv/thulu-releases verifica la release y copia imágenes (con sus firmas), chart, manifiesto firmado, bundle de deploy y binarios. En la instalación, OCTOPUS_UPDATE_SOURCE apunta a ese directorio (https://… o file://…) y OCTOPUS_IMAGE_REGISTRY al registry interno. Las firmas se verifican igual que con los originales. OCTOPUS_UPDATE_SOURCE=off apaga las búsquedas y la pantalla muestra “búsquedas apagadas”.

thulu update saca su propio snapshot antes de cambiar nada. Para backups periódicos está deploy/backup.sh, que arma un .tar.gz con un pg_dump y el directorio de datos (worktrees, repos, adjuntos, contadores de uso), legible sólo por su dueño.

Ventana de terminal
OCTOPUS_COMPOSE_FILE=deploy/docker-compose.yml ./deploy/backup.sh /var/backups/thulu

Programalo en cron, por ejemplo todos los días a las 3:

0 3 * * * cd /opt/thulu && OCTOPUS_COMPOSE_FILE=deploy/docker-compose.yml ./deploy/backup.sh /var/backups/thulu >> /var/log/thulu-backup.log 2>&1
Variable Para qué
OCTOPUS_BACKUP_KEEP Cuántos snapshots se guardan en total, los del script y los del updater juntos. Default 5. El snapshot de cada versión guardada no se borra nunca.
OCTOPUS_BACKUP_REMOTE usuario@host:dir: copia cada archivo por rsync sobre ssh. Un backup local no sobrevive a la pérdida del disco.
OCTOPUS_BACKUP_REMOTE_KEEP Cuántas copias quedan en el destino remoto. Default 14.
OCTOPUS_BACKUP_AGE_RECIPIENT Cifra la copia remota con age (la local no).

El dump tiene las credenciales guardadas cifradas con OCTOPUS_SECRETBOX_KEY, y no la clave. Guardá la clave en otro lado: sin ella, un backup restaurado no puede leer las credenciales.

El panel “Backups” de “Actualizaciones” lee el resultado que deja cada corrida del script: el último backup con su archivo y tamaño, cuántos snapshots se guardan contra OCTOPUS_BACKUP_KEEP, si la copia fuera del host anduvo, y el comando para restaurarlo. Si nunca corrió, el panel lo dice.

Para restaurar a mano: pará el daemon (docker compose -f deploy/docker-compose.yml stop octopus), corré RESTORE=1 OCTOPUS_COMPOSE_FILE=deploy/docker-compose.yml ./deploy/backup.sh <archivo.tar.gz> (pide confirmación antes de pisar el directorio de datos), volvé a levantar el stack y verificá /health/ready. Restaurar pisa /data: se pierde lo escrito después del backup.

Thulu borra lo que crea. Cada contenedor, pod, volumen y red que levanta lleva una etiqueta de Thulu, y la limpieza elige siempre por etiqueta, así que no toca otros proyectos del mismo host o cluster.

Qué Cuándo se va
Contenedor de un agente Al terminar de procesar su corrida. Si quedó alguno, el janitor lo borra 2 h después de que salió.
Contenedor de chat Tras el tiempo de inactividad, al borrar o reiniciar el chat, o 2 h después de salir.
Sandbox Cuando la tarea llega a una etapa final, al borrar la tarea, o 6 h después de que la tarea quedó fallida, bloqueada o sin turnos.
Worktree de la tarea En etapa final o al borrar la tarea. Una tarea trabada retention.stuck_worktree_days días se “estaciona”: los cambios pendientes se commitean en la rama de la tarea y se borra el checkout. Relanzarla retoma el trabajo.
Adjuntos, logs, salidas, uso y keys de una tarea Al borrar la tarea.
Todo lo de un workspace Al borrar el workspace (ver Workspaces y equipo).
Imágenes viejas de Thulu En cada pasada del janitor, salvo las de las versiones guardadas.

El janitor pasa cada hora, en Docker y en Kubernetes. Además mira el espacio libre del directorio de datos cada 5 minutos y, por debajo del umbral, limpia en el momento.

El operador los cambia en “Configuración → Salud → Retención y limpieza”, con el valor en días (0 = para siempre; un campo vacío vuelve al default). El panel también muestra la última pasada del janitor y el disco libre. Por API es PUT /settings/<clave>:

Ventana de terminal
curl -X PUT https://thulu.empresa.com/settings/retention.run_logs_days \
-H "Authorization: Bearer $OCTOPUS_API_KEY" -H "Content-Type: application/json" -d '60'
Clave Default Qué cubre
retention.run_logs_days 30 Logs y salidas de corridas, transcripciones de chat
retention.sessions_days 90 Filas de uso por corrida. El costo diario agregado se conserva.
retention.webhook_deliveries_days 30 Log de entregas de webhooks
retention.run_keys_days 7 Keys de corridas y chats vencidas o revocadas. Las de personas y operador no se borran.
retention.sandbox_rows_days 30 Filas de sandboxes y chats ya desarmados
retention.stuck_worktree_days 14 Cuándo se estaciona el worktree de una tarea trabada
retention.cache_metrics_days 90 Historial de la caché de dependencias (ver Infraestructura)
janitor.disk_free_min_percent 10 Porcentaje de disco libre que dispara una limpieza; 0 la apaga
audit.retention_days sin definir Registro de auditoría; opcional (ver Seguridad y auditoría)

Un log se borra a los 30 días y su fila de uso a los 90, así que en una corrida vieja el link al log deja de andar antes.

Lista lo que Thulu dejó en el host y nada usa: contenedores parados, imágenes viejas, caché de builds, snapshots fuera de retención. Cada lista viene agrupada en “Contenedores”, “Imágenes”, “Volúmenes” y “Redes”, con una sección “Se conservan” que dice por qué guarda cada imagen.

Ventana de terminal
thulu gc # sólo muestra
thulu gc -apply # borra lo listado
thulu gc -unclaimed # suma imágenes de Thulu que ninguna versión reclama

thulu export empaqueta un workspace en un .tgz: tableros con sus etapas y tipos, proyectos con sus entornos y el script de arranque vigente, métricas y dashboards, tareas con adjuntos, comentarios, reportes, eventos y casos de prueba, el catálogo de agentes y skills, los MCP registrados y la configuración de la instalación. Los ids se conservan, así que las referencias cruzadas siguen resolviendo.

No viajan los secretos (credenciales, claves LLM, tokens de ingesta), las personas y sus permisos, las API keys, el registro de auditoría, el uso y costos, los chats ni los logs de corridas. El bundle trae un checklist de los secretos que hay que volver a cargar.

Con la CLI descargada, export e import entran solos al contenedor del daemon (docker compose exec, o kubectl exec en Kubernetes), donde están la base y los adjuntos, así que el stack tiene que estar corriendo.

  1. En el host de origen, exportá. -workspace acepta el id o el slug, y con un solo workspace se puede omitir (thulu workspace list los lista):

    Ventana de terminal
    thulu export -workspace nube-andina -out nube-andina.tgz
  2. En el host de destino, creá el workspace si todavía no existe. El import usa por defecto el workspace del bundle; si ese id no existe acá, se detiene y pide -workspace:

    Ventana de terminal
    thulu workspace create -name "Nube Andina" -slug nube-andina -owner-email <email>
  3. Copiá el archivo al host de destino e importalo:

    Ventana de terminal
    thulu import -workspace nube-andina nube-andina.tgz

    El import respeta el orden de dependencias y se puede repetir: las tareas, proyectos y comentarios que ya existen quedan como están, así que un segundo intento termina un import cortado. Los tableros y los MCP que ya existen se pisan con lo del bundle. La configuración de la instalación de origen se aplica sólo con -with-settings, porque alcanza a todos los workspaces del destino. Al final imprime un resumen con lo que escribió, las advertencias y la lista de secretos para volver a cargar; -json da los conteos en JSON.

  4. Volvé a cargar a mano cada secreto del checklist en Credenciales y motores, reemití los tokens de ingesta y configurá ingest.public_url con la dirección del host nuevo (el import la saltea a propósito).

Ventana de terminal
thulu uninstall # muestra todo lo que se borraría
thulu uninstall -yes # baja el stack y borra contenedores, imágenes, red y configuración de Thulu
thulu uninstall -yes -purge-data # además la base, los volúmenes de datos, deploy/.env y los backups

Sin -purge-data quedan los volúmenes de datos, deploy/.env (tiene la contraseña de la base y la clave que cifra las credenciales) y los backups, así que una reinstalación vuelve a abrirlos. -purge-data no tiene vuelta atrás: exportá antes lo que necesites. En una instalación con Helm imprime los comandos helm uninstall y kubectl delete namespace en vez de correrlos. El binario thulu no se borra; el comando imprime su ruta.

  • “No se ofrecen porque fallaron la verificación”: la firma del manifiesto de esas versiones no coincide con la clave embebida. Pasa con un mirror que alguien modificó o con builds de preview sin cosign en el PATH. No las instales.
  • “Falló la última búsqueda”: el daemon no llegó a la fuente de releases. Detrás de un proxy, el daemon respeta HTTPS_PROXY. Con un repo privado, OCTOPUS_UPDATE_GITHUB_TOKEN.
  • El preflight se detiene con una diferencia de archivos: alguien editó deploy/docker-compose.yml. Pasá el cambio a docker-compose.override.yml y volvé a correr el update.
  • El historial no muestra la corrida que hiciste: el host no tenía OCTOPUS_API_KEY de operador, o la key no tiene rol admin o el permiso settings. La CLI lo avisa al final como advertencia.
  • “Todavía no hay versiones guardadas”: la lista de versiones guardadas arranca con el primer thulu apply. Una instalación que nunca se actualizó con la CLI no tiene a dónde volver sin un backup.