Instalación self-hosted
Thulu self-hosted es un binario (thulu) que instala y administra el resto: el daemon con el dashboard, Postgres y los servidores MCP que usan los agentes. El binario pesa unos 22 MB y trae adentro los archivos de despliegue, así que no hace falta clonar nada; el daemon corre desde la imagen octopus. thulu init es un asistente de terminal que pregunta, escribe la configuración, crea el primer workspace con una key para la CLI y levanta el stack. El instalador también deja octopus como alias del mismo comando.
Si preferís no instalar nada, mirá Empezar en SaaS: hoy es una beta cerrada por invitación.
Antes de empezar
Sección titulada «Antes de empezar»| Necesitás | Para qué |
|---|---|
| Linux o macOS, amd64 o arm64 | Es para lo que se compila la CLI. |
| Docker con Compose v2 (o un cluster de Kubernetes con Helm) | Corre el daemon, Postgres y los MCP. En macOS sirve Colima. |
| Opcional: una API key de Anthropic y un token de GitHub para los agentes | Sin la primera, las corridas fallan al despacharse. Sin el segundo, los agentes sólo leen repos públicos. Se pueden cargar después. |
Las imágenes se publican para linux/amd64 y linux/arm64, así que una Mac con Apple Silicon las corre de forma nativa.
Flujo recomendado: una máquina, sin login
Sección titulada «Flujo recomendado: una máquina, sin login»Es la forma más corta de ver Thulu andando en tu computadora. El dashboard queda publicado sólo en 127.0.0.1:8585 y se entra con un link que lleva un token.
-
Instalá el binario. El script detecta tu plataforma, baja la release, verifica el checksum y, si tenés
cosign, la firma:Ventana de terminal curl -fsSL https://github.com/MisterDonnuts/thulu/releases/latest/download/install.sh | shLo deja en un directorio de tu
PATHque se pueda escribir, nunca consudo. Si no hay ninguno, lo pone en~/.local/bine imprime la línea para sumarlo alPATH.--version 0.3.4instala una release puntual. Si hay una terminal, al final pregunta “¿Configurarlo ahora? (thulu init)”. -
Corré el asistente desde cualquier carpeta:
Ventana de terminal thulu initSi no estás dentro de un checkout, escribe los archivos de despliegue en
~/.octopus(o enOCTOPUS_HOME). Antes de la primera pregunta revisa la máquina: si Docker responde, si está Compose v2, la arquitectura, si los puertos 8585 y 5433 están libres y el contexto dekubectl. Las preguntas son formularios: flechas para moverte, espacio para marcar y Enter para confirmar. Con-plainpasan a ser texto con opciones numeradas. -
En “¿Cómo querés instalar?” dejá la opción por defecto, “Rápida, en esta máquina”: Docker con la imagen publicada, sin login y sólo en
127.0.0.1:8585. Pregunta tres cosas y el resto toma su valor por defecto:- Engines: marcá Claude Code y pegá una API key de Anthropic (empieza con
sk-ant-api). Enter la saltea y la cargás después en el dashboard. Amazon Bedrock y Google Vertex AI se configuran después, en Configuración → Motores de IA. - Runner: dejá “No, los agentes corren en contenedores” (ver Otros flujos).
- GitHub: el token para que los agentes clonen y pusheen. También se puede saltear.
- Engines: marcá Claude Code y pegá una API key de Anthropic (empieza con
-
Revisá el resumen. Muestra la instalación, el acceso, la base de datos, los servidores MCP, los engines y lo que quedó salteado. Nada se escribe antes de este punto. Elegí “Instalar”, “Ver los cambios de deploy/.env y el comando” (muestra el diff, con los secretos recortados, y vuelve a preguntar) o “Cancelar”.
-
Esperá a que levante. El asistente baja la imagen, detecta el grupo del socket de Docker, levanta Postgres, crea el workspace y la key, arranca el resto del stack y espera a que el daemon responda. El log completo queda en
deploy/init.log. Al final imprime el link del dashboard, la key y los próximos pasos:Thulu está andando.Dashboard http://localhost:8585/?token=…sólo 127.0.0.1, sin login: este link es la llave. `thulu open` lo vuelve a abrir.Key para la CLI y la API. Se muestra sólo esta vez, guardala:oct_…Usala con: export OCTOPUS_API_KEY=oct_6b77…La key vence el …Guardá la key en un gestor de secretos: es la que usa la CLI para hablar con el daemon y no se puede recuperar. Vence a los 90 días, salvo que hayas corrido
thulu initconOCTOPUS_API_KEY_MAX_TTL=0en el entorno. Para otra,thulu keys mint. -
Abrí el dashboard con
thulu open, o pegando el link del paso anterior. El botón “Configuración inicial” de la barra superior muestra cuántas cosas faltan.
Primer acceso a una instalación sin login. El número del botón Configuración inicial son los ítems que todavía faltan. -
Completá los pendientes desde “Configuración inicial” (ver más abajo). Para que un agente trabaje hacen falta la key del modelo, un proyecto, un agente y una columna con ese agente. Ningún agente viene instalado.
Lo que pregunta thulu init
Sección titulada «Lo que pregunta thulu init»En “¿Cómo querés instalar?” hay tres modos:
| Modo | Qué es |
|---|---|
| “Rápida, en esta máquina” | Docker con la imagen publicada, sin login y sólo en 127.0.0.1:8585. Pregunta engines, runner y GitHub. |
| “Docker personalizado” | Elegís cómo se publica, cómo entra la gente, la base de datos y los servidores MCP. Los agentes corren en contenedores de este Docker. |
| “Kubernetes / k3s” | Escribe los values del chart de Helm. Los agentes corren como pods aislados en microVMs (Kata por defecto). |
Fuera del modo rápido, el asistente avanza por secciones. Algunas sólo aparecen según lo que respondiste antes.
| Sección | Qué pregunta | Por defecto |
|---|---|---|
| ¿Cómo entra la gente? | “Identidad”: proveedor OIDC, OAuth app de GitHub o “Sin login (sólo local)”. Para OIDC: URL del issuer, Client ID, Client secret, texto del botón, dominios de email permitidos, claim de grupos y mapeo de grupos a roles. | “Sin login” en el stack sin dominio; OIDC en el de dominio con HTTPS, donde “Sin login” no se ofrece |
| Postgres | En Docker, si usar el Postgres que trae el stack. Si no, o en Kubernetes, el DSN de una base externa, y prueba si responde. | El Postgres del stack |
| Dirección pública | Dominio (sólo el host), las direcciones desde las que se abre el dashboard, la IP del proxy o ingress que tenés adelante y, en el stack sin dominio, la dirección donde escucha. Rechaza un dominio con esquema o ruta y un origen que no sea http(s)://host. Con OIDC u OAuth te da la redirect URI que tenés que registrar en el proveedor. |
localhost en Docker |
| Cluster y aislamiento | Sólo en Kubernetes: namespace, nombre del release de Helm, aislamiento de los agentes (kata-clh, gvisor o sin RuntimeClass) e imagen de los agentes. Avisa si la RuntimeClass no está instalada. |
octopus, kata-clh |
| Servidores MCP para los agentes | Un checklist de los MCP nativos (ver abajo). | octopus-api, argus-github y argus-browser |
| Licencia enterprise (opcional) | La clave de licencia. Sin ella corre la edición community. En Kubernetes es obligatoria (ver Kubernetes). | Se saltea |
| Primer workspace | Nombre del workspace (el slug sale del nombre) y el email del dueño. Con login, también tu email en el proveedor de identidad, para administrar el workspace. | Default; sin login, el email de tu git config |
| Engines | Qué CLI corre cada agente y su credencial. | Claude Code |
| Runner en esta máquina | Sólo en Docker: si los agentes también pueden correr en esta máquina, fuera de los contenedores. | No |
| GitHub | URL de GitHub Enterprise Server si la usás, y el token para que los agentes clonen y pusheen. | Se saltea |
En Engines la lista también ofrece Codex (con una key de OpenAI) y OpenCode (APIs compatibles con OpenAI). Los dos corren agentes y auditores; los mensajes a un agente mientras trabaja y el runner necesitan Claude Code. Mirá Credenciales y motores.
Dos secretos se generan solos y nunca se preguntan: el que firma las sesiones y el que cifra las credenciales guardadas. Volver a correr thulu init los conserva; si se perdiera el segundo, todas las credenciales guardadas quedarían ilegibles.
Los stacks de Docker
Sección titulada «Los stacks de Docker»En “Docker personalizado” el asistente ofrece dos stacks, los dos con la imagen publicada:
| Stack | Qué es | Cuándo |
|---|---|---|
“Servidor con dominio y HTTPS” (docker-compose.caddy.yml) |
Daemon, Postgres y Caddy adelante, que saca solo el certificado para tu dominio. Los puertos 80 y 443 tienen que llegar a la máquina. | Un servidor con dominio público. |
“Sólo en esta máquina, o detrás de tu propio proxy” (docker-compose.standalone.yml) |
La imagen y Postgres, sin nada adelante, en el puerto 8585. Escucha en 127.0.0.1 salvo que pongas otra dirección. |
Para probar en tu máquina, o detrás de un reverse proxy tuyo que termine el TLS. |
“Sin login” se ofrece sólo en el stack sin dominio, porque ahí el asistente puede dejar el puerto en 127.0.0.1. Si publicás ese stack en otra dirección, el resumen avisa que se sirve HTTP plano y deja pendiente el TLS.
El checklist de MCP
Sección titulada «El checklist de MCP»| Servidor | Qué hace | Por defecto |
|---|---|---|
octopus-api |
El tablero para los agentes: leer y actualizar tareas, comentarios y adjuntos. Actúa con la key propia de la corrida. | Marcado |
argus-github |
GitHub: pull requests, commits, ramas, corridas de Actions. | Marcado |
argus-browser |
Chromium headless (Playwright): navegar, hacer clic, capturas adjuntas a la tarea. | Marcado |
argus-cloud |
Métricas y logs de la nube en sólo lectura (GCP, AWS). Necesita una credencial de nube por entorno. | Sin marcar |
argus-figma |
Archivos, frames, renders y comentarios de Figma en sólo lectura. Necesita un token de Figma en Credenciales. | Sin marcar |
argus-sandbox no está en la lista porque viene dentro del daemon. Cada servidor marcado corre como un contenedor más del stack (en Kubernetes, como un Deployment del chart). Para cambiar la selección, volvé a correr thulu init: el checklist arranca con lo que ya está instalado. Qué tools concede cada servidor a cada agente se decide después, en el dashboard: ver MCPs.
Si desmarcás todos, los agentes sólo tienen las herramientas del sandbox: no pueden comentar en la tarea, ni abrir PRs, ni usar el navegador.
Primer acceso
Sección titulada «Primer acceso»Cómo se entra depende de lo que elegiste en Identidad.
El daemon guarda un token aleatorio. El link http://localhost:8585/?token=… lo cambia por una cookie de sesión que dura 30 días. Para volver a abrirlo:
thulu openImprime el link y abre el navegador. Lee el token de deploy/.env, así que corrélo desde la carpeta de la instalación o desde cualquier lugar si la instalación está en ~/.octopus. Si entrás a la dirección sin el token, el dashboard te manda al login con esta indicación:

Cualquier pedido con esa cookie se trata como owner. Por eso el puerto queda en 127.0.0.1 y no conviene publicarlo en otra dirección.
Entrás por la dirección del dominio (https://<dominio>) y la página de login muestra el botón con el texto que elegiste (“Continuar con Google”, por ejemplo).
Antes del primer login registrá en el proveedor la URL que imprimió el asistente:
- OIDC:
https://<dominio>/auth/oidc/callback - OAuth app de GitHub:
https://<dominio>/github/oauth/callback
El email del dueño que pide el asistente queda como dueño provisorio del workspace y nadie entra con él. Si en “Primer workspace” pusiste tu email del proveedor de identidad, el asistente te invita como dueño (“invitado como dueño de …: su primer inicio de sesión se lo pasa”) y al entrar el workspace pasa a ser tuyo.
Si lo salteaste, el primer login cae en “Tu cuenta está esperando acceso”. Como sos la primera persona en entrar, la pantalla muestra el comando que te invita con la key del asistente: corrélo en una terminal con OCTOPUS_API_KEY y OCTOPUS_OPERATOR_WORKSPACE exportadas (el valor de la segunda está en deploy/.env, o en extraEnv de los values de Helm) y volvé a entrar.

Quien entre después espera una invitación: en una instalación propia el login no crea workspaces personales, salvo con OCTOPUS_ALLOW_PERSONAL_WORKSPACES=1. Las invitaciones se hacen desde el dashboard (ver Workspaces y equipo). Para que cada persona nueva entre sola a un workspace, agregá a deploy/.env OCTOPUS_SSO_DEFAULT_WORKSPACE=<slug> (y opcionalmente OCTOPUS_SSO_DEFAULT_ROLE, que por defecto es viewer) y recreá el stack con docker compose -f deploy/<stack>.yml up -d. Volver a correr el asistente conserva esa variable.
Si dejaste vacía la lista de dominios permitidos, puede entrar cualquiera que el proveedor autentique. En un proveedor multi-tenant (Google, el endpoint común de Entra ID) eso es cualquiera con una cuenta.
Completar la Configuración inicial
Sección titulada «Completar la Configuración inicial»La página “Configuración inicial” (/onboarding/) lee el estado real de la instalación y lista lo que falta, qué pasa mientras tanto y dónde se termina. Un ítem que completás a mano después cuenta igual. El botón de la barra superior sólo lo ven los admins y desaparece cuando no queda nada. El Panel muestra los mismos ítems en “Primeros pasos”.

| Ítem | Qué falta si no está | Dónde se resuelve |
|---|---|---|
| Proveedor de identidad | Cualquiera que llegue al puerto es owner. | Volver a correr thulu init y elegir un proveedor. |
| Workspace | No se puede crear nada. | thulu workspace create (ver CLI). |
| Key del modelo (BYOK) | Las corridas fallan al despacharse. | Configuración → Motores de IA. Ver Credenciales y motores. |
| Credencial de git | Los agentes sólo leen repos públicos: ni ramas ni PRs. | Credenciales o la GitHub App. |
| Primer proyecto | No hay repositorio en el que trabajen los agentes. | Proyectos: un repo de GitHub o una carpeta subida. |
| Definiciones de agentes | El tablero no tiene agentes a los que despachar. | Agentes, o un JSON desde Workshop → Importar archivo. |
| Una columna que corre un agente | Mover una tarjeta no lanza nada. | “Agente por defecto” en el Comportamiento de la columna (Tableros y etapas). |
| Agentes en la plantilla de cada proyecto | Las tareas de ese proyecto nacen sin agente. | La “Plantilla de etapas” del proyecto. |
Los dos primeros sólo los ve un admin del workspace operador.
Otros flujos
Sección titulada «Otros flujos»Un servidor con dominio y login
Sección titulada «Un servidor con dominio y login»-
Registrá una aplicación en tu proveedor de identidad (Okta, Entra ID, Keycloak, Google…) y anotá el issuer, el Client ID y el Client secret. Si tu proveedor sólo habla SAML, poné adelante un intermediario que hable OIDC, como Keycloak, y usá el issuer de ese intermediario.
-
Apuntá el DNS del dominio al servidor y dejá que lleguen los puertos 80 y 443.
-
En el servidor, corré
thulu init, elegí “Docker personalizado” y el stack “Servidor con dominio y HTTPS”. -
En Identidad elegí el proveedor OIDC. El asistente se conecta al issuer antes de seguir, así un error de tipeo falla en ese momento:
URL del issuer: ✓ el proveedor responde: login en https://accounts.google.com/o/oauth2/v2/auth -
Completá Client ID, Client secret, el texto del botón y los dominios de email permitidos (por ejemplo
tuempresa.com). El claim de grupos y el mapeogrupo:rolson opcionales: sin mapeo, cada persona entra con el rol por defecto y a los admins se los promueve a mano. -
En Dirección pública poné el dominio, sin
https://. El asistente imprime la redirect URI: registrala en el proveedor antes del primer login. -
En “Primer workspace” dejá el email del dueño provisorio que propone y, en la pregunta siguiente, poné tu email del proveedor de identidad. Tiene que ser otro: el asistente rechaza el del dueño provisorio.
-
Terminá el resto como en el flujo recomendado, guardá la key y entrá por
https://<dominio>cuando resuelvan el DNS y el certificado.
Un runner en esta máquina
Sección titulada «Un runner en esta máquina»En Docker, el asistente pregunta si los agentes también pueden correr en tu máquina, fuera de los contenedores, con tu git, tus herramientas y tu sesión de claude. Por ahora el runner corre sólo Claude Code, se configura sólo desde la CLI (no tiene pantalla en el dashboard) y en Kubernetes no se ofrece. Si respondés que sí, elegís las carpetas en las que puede trabajar (cada una tiene que ser un repo git y queda como un proyecto cuyo código no sale de esa máquina) y si dejarlo corriendo como servicio. Ver CLI.
Kubernetes
Sección titulada «Kubernetes»En Kubernetes los agentes corren como pods aislados en microVMs (Kata) y el daemon no toca ningún socket de Docker. Este runtime está validado en un k3s de un solo nodo con Kata. Pide una licencia que incluya kubernetes: sin ella el daemon no arranca. El cluster tiene que estar preparado antes: Kata instalado con su RuntimeClass, los namespaces de tenants y una base Postgres externa (el chart no trae base). La preparación y las variables están en docs/enterprise-setup.md, con la lista para instalaciones sin internet.
Hay dos formas de instalar el chart:
- Con
thulu init, eligiendo “Kubernetes / k3s”. Pregunta lo mismo que en Docker más el namespace, el release y el aislamiento, escribedeploy/values-<release>.yamly correhelm upgrade --install … --wait --timeout 10m. Ese archivo tiene el secreto JWT, la clave de credenciales y el secreto OIDC en texto plano: dejalo fuera de git. - Con
helm installdirecto sobredeploy/helm/octopus, con tus propios values (hay ejemplos envalues-production.example.yaml). En ese caso el workspace y la key se crean a mano con la CLI.
Las imágenes de agente publicadas no traen Claude Code: lo instalan desde npm la primera vez. En Kubernetes la instalación construye su propia imagen de agente con Claude Code adentro (un Job de BuildKit, una vez por versión) y hasta que termina los pods lo bajan al arrancar. Sin salida a npm hay que construir la imagen con CLAUDE_CODE_INSTALL=build; los pasos están en docs/enterprise-setup.md. Con los agentes en Kubernetes el token de suscripción de Claude (sk-ant-oat…) queda apagado: usá una API key de Anthropic, Bedrock o Vertex.
Volver a correr el asistente
Sección titulada «Volver a correr el asistente»thulu init es la forma de cambiar una respuesta: cada pregunta toma como default lo que la instalación ya tiene (de deploy/.env y de deploy/.octopus-init.json, que guarda las respuestas sin secretos). No vuelve a emitir la key si el workspace ya tiene una activa; una vencida no cuenta.
| Opción | Qué hace |
|---|---|
-dry-run |
Pregunta todo y muestra el resumen sin escribir ni ejecutar nada. |
-non-interactive |
Toma todos los defaults, o las respuestas de la corrida anterior. Para repetir una instalación o en CI. |
-plain |
Preguntas en texto con opciones numeradas, en lugar de formularios. |
-stack <archivo> |
Elige el stack de compose sin preguntarlo. |
-root <dir> |
Dónde vive la instalación. Por defecto se busca hacia arriba desde la carpeta actual, y si no hay ninguna, ~/.octopus. |
Problemas comunes
Sección titulada «Problemas comunes»- “no se llega desde acá” en la sección Postgres: la configuración se escribe igual, pero el workspace y la key quedan pendientes. El resumen imprime el comando para crearlos desde el contenedor del daemon cuando la base responda (en compose,
docker compose -f deploy/<stack>.yml exec octopus ./octopus workspace create …y después… ./octopus keys mint …). - Perdiste la key o venció a los 90 días: sólo se guarda su hash. Emití una nueva con
thulu keys mint, o desde el contenedor del daemon:docker compose -f deploy/<stack>.yml exec octopus ./octopus keys mint -workspace <id> -role owner -permissions full(más en CLI). - La primera corrida tarda más: en Docker, Claude Code se instala desde npm la primera vez que lo usa cada workspace y queda en un volumen. Sin salida a npm hay que construir la imagen de agente con
CLAUDE_CODE_INSTALL=build(docs/enterprise-setup.md). - Los agentes no tienen acceso al tablero ni a GitHub: revisá que los MCP estén marcados en el checklist y concedidos al agente en MCPs.
- Después del login con OIDC aparece “Tu cuenta está esperando acceso” y no sos la primera persona: nadie te invitó al workspace. Pedile a un admin que te invite, cerrá sesión y volvé a entrar.