313 313
314Para cada sesión que usa git gestionado por Anthropic, el runner también registra una línea `[runner:warn]` que comienza con `governed git: GIT_SSL_CAINFO is set` o `governed git: GIT_SSL_NO_VERIFY is set`. La línea dice qué hizo el runner con esa variable para su propio git, para git dentro de la sesión, y para sus hooks de ciclo de vida. Termina con si necesita cambiar algo.314Para cada sesión que usa git gestionado por Anthropic, el runner también registra una línea `[runner:warn]` que comienza con `governed git: GIT_SSL_CAINFO is set` o `governed git: GIT_SSL_NO_VERIFY is set`. La línea dice qué hizo el runner con esa variable para su propio git, para git dentro de la sesión, y para sus hooks de ciclo de vida. Termina con si necesita cambiar algo.
315 315
316### Agrega repositorios de github.com a sesiones de canales de Claude Tag
317
318Si Claude no puede agregar un repositorio de github.com a una sesión de un canal de Claude Tag, asegúrate de que el canal tenga acceso a él, incluso cuando tu runner pueda clonarlo. Ese acceso proviene de ajustes en GitHub y en tu organización de Claude, no de las credenciales de git de tu runner.
319
320**1. Instala la Claude GitHub App**
321
322Instala la [Claude GitHub App](https://github.com/apps/claude) en la organización o cuenta de GitHub propietaria del repositorio, e incluye ese repositorio en la instalación.
323
324**2. Vincula la organización o cuenta de GitHub**
325
326En [**Organization settings > Git providers**](https://claude.ai/admin-settings/source-control), vincula esa organización o cuenta a tu organización de Claude. Un Owner de tu organización de Claude que también sea propietario de la organización de GitHub puede vincularla. Consulta [Vincula tu organización de GitHub](https://claude.com/docs/claude-tag/admins/configure-github#link-your-github-organization).
327
328**3. Concede el repositorio**
329
330En la [configuración de administración de Claude Tag](https://claude.ai/admin-settings/claude-tag), concede el repositorio al canal o a un alcance más amplio que incluya el canal: su espacio de trabajo de Slack o todo Slack. Alguien con el rol Owner o el permiso Claude Tag Admin puede concederlo. Un administrador del canal también puede agregarlo a su propio canal si su cuenta de GitHub es administradora del repositorio. Consulta [Concede acceso al repositorio](https://claude.com/docs/claude-tag/admins/configure-github#grant-repository-access).
331
332Luego inicia un nuevo hilo en el canal y [nombra el repositorio](https://claude.com/docs/claude-tag/admins/configure-github#how-granted-repositories-reach-a-session) en tu primer mensaje.
333
334Si Claude sigue sin poder agregar el repositorio, consulta [Si Claude no puede llegar a un repositorio](https://claude.com/docs/claude-tag/admins/configure-github#if-claude-can%E2%80%99t-reach-a-repository) para saber dónde revisar el vínculo de GitHub y la concesión del repositorio.
335
316### Reescriba URLs de git para redes privadas336### Reescriba URLs de git para redes privadas
317 337
318Las URLs de repositorio llegan desde el plano de control como HTTPS, con el nombre de host de tu host de git; para GitHub Enterprise, es el nombre de host que configuraste para la [integración de GitHub Enterprise](https://code.claude.com/docs/es/github-enterprise-server.md) en claude.ai. Dos flags repetibles reescriben esas URLs antes del clon:338Las URLs de repositorio llegan desde el plano de control como HTTPS, con el nombre de host de tu host de git; para GitHub Enterprise, es el nombre de host que configuraste para la [integración de GitHub Enterprise](https://code.claude.com/docs/es/github-enterprise-server.md) en claude.ai. Dos flags repetibles reescriben esas URLs antes del clon:
376 396
377Las compilaciones y pruebas son generalmente la parte más grande y variable de la carga de una sesión, así que ejecute una compilación representativa de su repositorio, mida su CPU y memoria máximas, y aumente cualquier valor inicial que no deje espacio para el proceso de Claude Code en la parte superior de ese pico.397Las compilaciones y pruebas son generalmente la parte más grande y variable de la carga de una sesión, así que ejecute una compilación representativa de su repositorio, mida su CPU y memoria máximas, y aumente cualquier valor inicial que no deje espacio para el proceso de Claude Code en la parte superior de ese pico.
378 398
379El runner usa `--capacity` para limitar cuántas sesiones ejecuta a la vez. No divide CPU o memoria entre ellas, por lo que las sesiones en un runner comparten la CPU y memoria del contenedor. Para limitar la parte de una sesión, aplique límites desde su [script de envoltura](https://code.claude.com/docs/es/self-hosted-environments-configuration.md#wrapper-scripts). Lo que dar a un contenedor depende de cuántas sesiones sirve a la vez:399El runner usa `--capacity` para limitar cuántas sesiones ejecuta a la vez. No divide CPU ni memoria entre ellas, por lo que las sesiones en un runner comparten la CPU y la memoria del contenedor. Para evitar que el uso de memoria de una sesión termine las demás, ejecuta una sesión por runner con [su propio límite de memoria](#give-each-session-its-own-memory-limit).
380 400
381* **Una sesión por runner**: dé a cada contenedor los valores de una sesión. Use este dimensionamiento en `--capacity 1`, que la [sección de endurecimiento](#harden-your-deployment) recomienda, y para [runners bajo demanda](https://code.claude.com/docs/es/self-hosted-environments-configuration.md#on-demand-runners), donde establece los valores en la carga de trabajo que su [hook `spawn-runner`](https://code.claude.com/docs/es/self-hosted-environments-configuration#the-spawn-runner-hook) envía, como la plantilla de pod de un Job de Kubernetes.401* **Una sesión por runner**: dé a cada contenedor los valores de una sesión. Use este dimensionamiento en `--capacity 1`, que la [sección de endurecimiento](#harden-your-deployment) recomienda, y para [runners bajo demanda](https://code.claude.com/docs/es/self-hosted-environments-configuration.md#on-demand-runners), donde establece los valores en la carga de trabajo que su [hook `spawn-runner`](https://code.claude.com/docs/es/self-hosted-environments-configuration#the-spawn-runner-hook) envía, como la plantilla de pod de un Job de Kubernetes.
382* **Varias sesiones por runner**: en un `--capacity` por encima de uno, multiplique los valores de una sesión por la capacidad, porque hasta esa cantidad de sesiones pueden ejecutarse en el contenedor a la vez. Las recetas de [Kubernetes](#kubernetes) y [Docker Compose](#docker-compose) ejecutan `--capacity 4` sin límites de CPU o memoria, así que agregue límites dimensionados para la capacidad que ejecuta.402* **Varias sesiones por runner**: en un `--capacity` por encima de uno, multiplique los valores de una sesión por la capacidad, porque hasta esa cantidad de sesiones pueden ejecutarse en el contenedor a la vez. Las recetas de [Kubernetes](#kubernetes) y [Docker Compose](#docker-compose) ejecutan `--capacity 4` sin límites de CPU o memoria, así que agregue límites dimensionados para la capacidad que ejecuta.
403* **Varios contenedores de runner en un host**: dimensiona el host para la suma de las capacidades de los runners, porque cada runner cuenta solo sus propias sesiones contra `--capacity`:
404
405 * **Memoria**: multiplica la memoria de una sesión por la suma y luego agrega lo que usan los propios procesos del host
406 * **CPU**: multiplica la solicitud de CPU de una sesión por la suma. Para permitir que todas las sesiones superen su solicitud a la vez, multiplica en su lugar su límite de CPU.
407
408 Da a cada runner su propio contenedor. Usa la [misma ruta de `--base-dir`](#keep-the-base-directory-and-capacity-identical-across-runners) en cada uno, en un almacenamiento que ningún otro contenedor de runner monte, para que cada runner tenga [su propio directorio de trabajo](#harden-your-deployment).
409
410### Da a cada sesión su propio límite de memoria
411
412Para evitar que una sesión termine las demás cuando se agota la memoria, ejecuta cada runner en `--capacity 1` bajo su propio límite de memoria, establecido en la memoria de una sesión:
413
414* **En un contenedor**: ejecuta un runner por contenedor y establece el límite de memoria del contenedor, como `limits.memory` en Kubernetes, `--memory` en Docker o `mem_limit` en un archivo de Compose. Mantén la suma de los límites de los contenedores por debajo de la memoria del host, porque cuando el propio host se queda sin memoria, el kernel puede matar procesos en cualquier parte del host.
415* **Bajo systemd sin contenedor**: establece `MemoryMax` en la unidad del runner y ejecuta un runner por host, porque los runners que [usan el mismo `--base-dir`](#keep-the-base-directory-and-capacity-identical-across-runners) en un sistema de archivos comparten los repositorios clonados allí. Por ejemplo, en `--capacity 1` sin un hook `checkout`, sus sesiones sobre un mismo repositorio pueden trabajar en el mismo checkout a la vez. Para ejecutar varios runners en un host, da a cada uno un contenedor.
416
417Cuando un contenedor o una unidad alcanza su límite de memoria, el kernel mata procesos solo dentro de él. Si estableces `--capacity` mayor que uno, las sesiones comparten un límite. Cuando lo alcanzan, el kernel no elige por sesión qué procesos matar, por lo que el uso de memoria de una sesión puede terminar las demás.
418
419En Kubernetes, no puedes separar las sesiones con ninguno de los valores del ajuste `singleProcessOOMKill` del kubelet. Tampoco puedes limitar la memoria total de una sesión con un `ulimit` de memoria en tu [script de envoltura](https://code.claude.com/docs/es/self-hosted-environments-configuration.md#wrapper-scripts). El kernel aplica un valor como `ulimit -v` a cada proceso por separado.
420
421### Deja margen bajo los límites de procesos e hilos
422
423Para dimensionar un límite de procesos e hilos, mide lo que necesita una sesión y multiplícalo por el número de sesiones que comparten el límite. Cuando esas sesiones alcanzan el límite, el kernel rechaza nuevos procesos e hilos, por lo que los comandos, compilaciones y pruebas en cualquiera de ellas pueden fallar. La llamada rechazada devuelve `EAGAIN`, cuyo mensaje estándar es `Resource temporarily unavailable`.
424
425Supón que una sesión alcanza un pico de 300 procesos e hilos, y cuatro sesiones comparten el límite. Juntas necesitan 1,200, y con un límite de 2,400, dejas margen para compilaciones que necesiten el doble.
426
427#### Dónde establecer cada límite
428
429Cada límite de esta tabla cuenta tanto hilos como procesos, y `pids.max` es el archivo que contiene el límite de un cgroup:
430
431| Límite | Dónde lo estableces | Qué lo comparte |
432| :- | :- | :- |
433| `pids.max` en Kubernetes | `podPidsLimit` en la configuración del kubelet de los nodos que ejecutan runners. No puedes establecerlo en una especificación de Pod. | Los contenedores de un Pod |
434| `pids.max` en Docker | `--pids-limit`, o `pids_limit` en un archivo de Compose | Un contenedor |
435| `pids.max` bajo systemd | `TasksMax` en la unidad del runner | Una unidad |
436| `ulimit -u` | El shell o servicio que inicia el runner | Todo lo que en el host se ejecuta con el ID de usuario del runner. No se aplica para root. |
437
438Cuando no estableces `TasksMax`, systemd aplica `DefaultTasksMax`, que de forma predeterminada es el 15% del menor entre `kernel.pid_max`, `kernel.threads-max` y el `pids.max` del cgroup raíz.
439
440#### Mide el pico de una sesión
441
442Para obtener los números de tus propios runners, mide una sesión en su host, que en Kubernetes es el nodo:
443
444**1. Inicia un runner solo**
445
446Inicia un runner en `--capacity 1` sin nada más en su contenedor o unidad de systemd, solo en su host y en su entorno:
447
448* **Su host**: usa uno que no ejecute ningún otro runner. Así podrás identificar su proceso en el siguiente paso.
449* **Su entorno**: haz que sea el único runner allí, por ejemplo en un entorno que [crees para la prueba](https://code.claude.com/docs/es/self-hosted-environments-quickstart.md#set-up-manually). Las sesiones se enrutan a un entorno y no a un runner.
450
451No inicies una sesión todavía, porque necesitas el directorio del siguiente paso antes de que comience la compilación.
452
453**2. Encuentra el directorio de cgroup del runner**
454
455En el host, no dentro del contenedor, encuentra el ID de proceso del runner, por ejemplo con `ps`. Lee `/proc/<pid>/cgroup` para ese ID:
456
457* **Una línea contiene `:pids:`**: el host usa cgroup v1, y el directorio es `/sys/fs/cgroup/pids` seguido del texto que aparece después de `:pids:`
458* **La única línea comienza con `0::`**: el host usa cgroup v2, y el directorio es `/sys/fs/cgroup` seguido del texto que aparece después de `0::`
459
460**3. Ejecuta una compilación representativa y registra el pico**
461
462Ejecuta una sesión a lo largo de una compilación representativa y toma el pico del directorio de cgroup del runner:
463
464* **El directorio tiene `pids.peak`**: lee el archivo cuando termine la compilación
465* **El directorio no tiene `pids.peak`**: lee `pids.current` una vez por segundo mientras se ejecuta la compilación y conserva el valor más alto. Un pico entre dos lecturas pasa desapercibido, así que trata ese valor como una estimación baja.
466
467**4. Multiplica por las sesiones que comparten el límite**
468
469* **El Pod, contenedor o unidad de un runner**: multiplica el pico por el `--capacity` del runner
470* **Un límite que comparten varios runners**: multiplica el pico por la suma de sus capacidades
471
472Luego agrega el pico de cualquier otra cosa que comparta el límite, como otro contenedor en el Pod.
473
474**5. Establece el límite por encima del resultado**
475
476Con un límite más alto, dejas margen para compilaciones más pesadas, y con uno más bajo, detienes antes una fork bomb.
477
478#### Comprueba un límite `pids.max`
479
480* **Comprueba si se alcanzó `pids.max`**: busca en el registro del kernel del host, por ejemplo con `dmesg`, una línea que contenga `cgroup: fork rejected by pids controller in` y la ruta de un cgroup. El kernel la registra solo la primera vez que rechaza un proceso o hilo en ese cgroup, así que busca en todo el registro.
481* **Consulta el límite vigente**: lee `pids.max` en el directorio de cgroup del runner y en cada directorio por encima de él. El valor más bajo es el límite vigente, y `max` significa que no hay límite en ese nivel.
383 482
384## Kubernetes483## Kubernetes
385 484
577 676
578Lo que la ruta de reutilización hace y no garantiza:677Lo que la ruta de reutilización hace y no garantiza:
579 678
580* **Cualquier forma de clon funciona**: un clon completo, superficial, o de rama única en la ruta se usa tal cual. El runner nunca pasa `--depth` cuando obtiene en un clon existente, por lo que un precalentamiento completo mantiene su historial completo y uno superficial permanece superficial. `CLAUDE_RUNNER_FETCH_DEPTH` (`full`, `0`, o un número; valor predeterminado 50) controla solo el clon frío que el runner hace cuando no existe clon aún.679* **Profundidad del clon**: crea el clon sin `--depth`, con o sin `--single-branch`. El runner nunca pasa `--depth` cuando obtiene en un clon con un remoto `origin`, incluso si configuras [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/es/self-hosted-environments-reference#environment-variable-only-settings). Con un clon completo, cada sesión descarga solo los commits que le faltan al clon. Con un clon superficial, una sesión en una rama que se bifurcó antes del commit más antiguo del clon descarga todo el historial de esa rama.
581* **Los cambios rastreados se reinician, los archivos sin rastrear persisten**: con `--capacity 1`, cada sesión comienza desde un reinicio duro que borra las modificaciones rastreadas de la sesión anterior, pero el runner nunca ejecuta `git clean`, por lo que los archivos sin rastrear de las sesiones anteriores del propietario bloqueado permanecen en el árbol.680* **Los cambios rastreados se reinician, los archivos sin rastrear persisten**: con `--capacity 1`, cada sesión comienza desde un reinicio duro que borra las modificaciones rastreadas de la sesión anterior, pero el runner nunca ejecuta `git clean`, por lo que los archivos sin rastrear de las sesiones anteriores del propietario bloqueado permanecen en el árbol.
582* **Directorios por sesión persisten también**: junto al checkout, el runner crea entradas por sesión bajo `<base-dir>/_sessions/` para cada sesión que ejecuta. El directorio de configuración de Claude de la sesión contiene una copia local de la transcripción de la conversación. Junto a él se encuentran los archivos cargados de la sesión, cuando la sesión tiene alguno. El directorio de sesión también se encuentra allí: contiene cualquier worktree por sesión y checkouts de hook `checkout` mientras se ejecuta la sesión, y mantiene cualquier otra cosa que Claude escribió en él.681* **Directorios por sesión persisten también**: junto al checkout, el runner crea entradas por sesión bajo `<base-dir>/_sessions/` para cada sesión que ejecuta. El directorio de configuración de Claude de la sesión contiene una copia local de la transcripción de la conversación. Junto a él se encuentran los archivos cargados de la sesión, cuando la sesión tiene alguno. El directorio de sesión también se encuentra allí: contiene cualquier worktree por sesión y checkouts de hook `checkout` mientras se ejecuta la sesión, y mantiene cualquier otra cosa que Claude escribió en él.
583 682
587* **Con el proxy de git, un precalentamiento con `reftable` pierde sus refs**: cuando el runner sanitiza `.git/` antes de cada sesión, mantiene `.git/refs/` y elimina el directorio `.git/reftable/`. Un clon precalentado con el formato de refs `reftable` de git pierde así sus refs, por lo que debes precalentar el clon con el formato de refs `files` de git en su lugar.686* **Con el proxy de git, un precalentamiento con `reftable` pierde sus refs**: cuando el runner sanitiza `.git/` antes de cada sesión, mantiene `.git/refs/` y elimina el directorio `.git/reftable/`. Un clon precalentado con el formato de refs `reftable` de git pierde así sus refs, por lo que debes precalentar el clon con el formato de refs `files` de git en su lugar.
588* **Los clones largos no necesitan solución alternativa**: el runner limita cada operación de git con un vigilante de 120 segundos sin progreso y un límite duro de 30 minutos, no un tiempo de espera plano, por lo que un clon frío lento que sigue reportando progreso se completa.687* **Los clones largos no necesitan solución alternativa**: el runner limita cada operación de git con un vigilante de 120 segundos sin progreso y un límite duro de 30 minutos, no un tiempo de espera plano, por lo que un clon frío lento que sigue reportando progreso se completa.
589 688
689### Confirma que el runner usa tu clon
690
691Para confirmar que el runner usa tu clon, inícialo con [`--log-level debug`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags) e inicia una sesión en el repositorio. El runner registra `Prefetched repo found at` y la ruta del clon. Si en cambio registra `No prefetched repo, fresh init at`, no encontró ningún clon utilizable en la ruta de esa línea. Comprueba que tu clon esté en esa ruta, que pertenezca al usuario del runner y que tenga un remoto llamado `origin`.
692
693Si el runner encuentra tu clon y las sesiones siguen iniciándose lentamente, comprueba si el clon es superficial.
694
590## Fije la versión695## Fije la versión
591 696
592De forma predeterminada, el proceso hijo de cada sesión ejecuta el binario propio del runner, y el runner desactiva la actualización automática dentro de las sesiones que genera. Con ese valor predeterminado, cada sesión ejecuta la versión que instalaste en el host o que incluiste en la imagen. Una actualización a nivel de host entra en vigor la próxima vez que el runner se inicia.697De forma predeterminada, el proceso hijo de cada sesión ejecuta el binario propio del runner, y el runner desactiva la actualización automática dentro de las sesiones que genera. Con ese valor predeterminado, cada sesión ejecuta la versión que instalaste en el host o que incluiste en la imagen. Una actualización a nivel de host entra en vigor la próxima vez que el runner se inicia.
609* **Flota fija**: ejecute un conjunto estático de réplicas de runner y escale en las [métricas de Prometheus](https://code.claude.com/docs/es/self-hosted-environments-reference.md#prometheus-metrics) que cada runner sirve714* **Flota fija**: ejecute un conjunto estático de réplicas de runner y escale en las [métricas de Prometheus](https://code.claude.com/docs/es/self-hosted-environments-reference.md#prometheus-metrics) que cada runner sirve
610* **Runners bajo demanda**: ejecute el subcomando `claude self-hosted-runner orchestrator`, que sondea Anthropic para sesiones que están en cola sin runner disponible e invoca su hook `spawn-runner` para arrancar uno por sesión. Consulte [Runners bajo demanda](https://code.claude.com/docs/es/self-hosted-environments-configuration.md#on-demand-runners).715* **Runners bajo demanda**: ejecute el subcomando `claude self-hosted-runner orchestrator`, que sondea Anthropic para sesiones que están en cola sin runner disponible e invoca su hook `spawn-runner` para arrancar uno por sesión. Consulte [Runners bajo demanda](https://code.claude.com/docs/es/self-hosted-environments-configuration.md#on-demand-runners).
611 716
717### Libera las sesiones inactivas en una flota fija
718
719Si las sesiones permanecen en cola en una flota fija mientras todos los runners están en línea, la causa pueden ser las sesiones inactivas. De forma predeterminada, una sesión conserva su espacio después de que termina su turno, y su runner permanece bloqueado para el [propietario](https://code.claude.com/docs/es/self-hosted-environments#key-concepts) de esa sesión. Si la [tarea programada en la nube](https://code.claude.com/docs/es/routines.md) de tu equipo inicia una nueva sesión en cada ejecución, sus sesiones inactivas pueden llenar todos los espacios.
720
721Para comprobarlo, lee `claude_code_self_hosted_runner_session_idle_seconds` en las [métricas de Prometheus](https://code.claude.com/docs/es/self-hosted-environments-reference.md#prometheus-metrics) de cada runner. Cada sesión que mantiene un runner tiene su propia serie: `0` mientras la sesión está ocupada; de lo contrario, los segundos transcurridos desde que quedó inactiva. Elige el tiempo de inactividad que permitirías: más largo que las pausas que las personas hacen a mitad de una tarea, y lo bastante corto como para que cada ejecución se libere antes de que comience la siguiente de la tarea programada. Compara las series con ese valor.
722
723* **Series por encima de ese tiempo, como `1800` para 30 minutos**: las sesiones inactivas ocupan esos espacios, así que establece `--release-idle-session-min`
724* **Todas las series por debajo**: ninguna sesión ha estado inactiva tanto tiempo. Agrega runners o establece [`--kill-session-after-min`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags) para las [sesiones que nunca quedan inactivas](#some-sessions-don’t-count-as-idle).
725* **Un runner sin series**: ninguna sesión lo ocupa. Consulta **Sessions stay queued** en [Solución de problemas](#troubleshooting).
726* **Un runner cuyas métricas no responden**: es posible que su proceso haya terminado. Consulta **An environment that looks healthy** en [Cuando el runner termina](#when-the-runner-exits).
727
728Para liberar las sesiones inactivas, inicia cada runner con [`--release-idle-session-min`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags) y ese tiempo en minutos, como `--release-idle-session-min 30`. El runner libera entonces cualquier sesión que permanezca inactiva ese tiempo, ya sea que la haya iniciado una tarea programada o una persona.
729
730* **Trabajo sin enviar**: una sesión liberada se reanuda con su siguiente mensaje, sin el trabajo que no había enviado con push. Para conservar sus commits, consulta [Las sesiones reanudadas pierden el trabajo sin enviar](#additional-limitations).
731* **El runner termina**: un runner que libera su última sesión inicia una [salida normal](#when-the-runner-exits). Haz que tu supervisor lo reinicie en un sistema de archivos limpio, como se describe en [Refuerza tu despliegue](#harden-your-deployment).
732* **Sesiones que nunca quedan inactivas**: establece también [`--kill-session-after-min`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags), porque [algunas sesiones no cuentan como inactivas](#some-sessions-don’t-count-as-idle)
733
612## Problemas conocidos y limitaciones734## Problemas conocidos y limitaciones
613 735
614Las siguientes son las limitaciones en esta versión, con soluciones alternativas donde exista una.736Las siguientes son las limitaciones en esta versión, con soluciones alternativas donde exista una.
642### Limitaciones adicionales764### Limitaciones adicionales
643 765
644* **Las sesiones reanudadas pierden el trabajo sin push**: un runner nuevo vuelve a clonar el repositorio desde su rama inicial, por lo que el trabajo al que la sesión no había hecho push se pierde.766* **Las sesiones reanudadas pierden el trabajo sin push**: un runner nuevo vuelve a clonar el repositorio desde su rama inicial, por lo que el trabajo al que la sesión no había hecho push se pierde.
645 * **Para conservar el trabajo con commit**: establece [`--push-outcome-on-release`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags) en todos los runners del entorno, porque un runner sin el flag reanuda la sesión desde su rama inicial. Un runner con el flag hace, en la medida de lo posible, push de las ramas de resultado de la sesión antes de liberarla, y la sesión reanudada comienza desde esos commits. El push usa las credenciales de git propias del host del runner, incluso en un runner que usa [git administrado por Anthropic](#use-the-anthropic-git-proxy). Los cambios sin commit se siguen perdiendo.767 * **Para conservar el trabajo con commit**: establece [`--push-outcome-on-release`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags) en todos los runners del entorno, porque un runner sin el flag reanuda la sesión desde su rama inicial. Un runner con el flag hace, en la medida de lo posible, push de las ramas de resultado de la sesión antes de liberarla, y la sesión reanudada comienza desde esos commits. El push usa las credenciales de git propias del host del runner, incluso en un runner que usa [git administrado por Anthropic](#use-the-anthropic-git-proxy).
646 * **Con un hook `checkout`**: a los repositorios obtenidos mediante un [hook de ciclo de vida `checkout`](https://code.claude.com/docs/es/self-hosted-environments-configuration#checkout) no se les hace push. En su lugar, guarda una instantánea de ellos desde el [hook `post-session`](https://code.claude.com/docs/es/self-hosted-environments-configuration#post-session).768 * **Con un hook `checkout`**: a los repositorios obtenidos mediante un [hook de ciclo de vida `checkout`](https://code.claude.com/docs/es/self-hosted-environments-configuration#checkout) no se les hace push. En su lugar, guarda una instantánea de ellos desde el [hook `post-session`](https://code.claude.com/docs/es/self-hosted-environments-configuration#post-session).
647 * **Antes de habilitar el flag**: restringe quién puede hacer push a las refs `claude/*` en el remoto de origen. Al reanudar, el runner obtiene la rama a la que se hizo push previamente sin verificar quién hizo el push.769 * **Antes de habilitar el flag**: restringe quién puede hacer push a las refs `claude/*` en el remoto de origen. Al reanudar, el runner obtiene la rama a la que se hizo push previamente sin verificar quién hizo el push.
770 * **Los cambios sin commit se siguen perdiendo**: para pedirle a Claude que haga commit y push cuando termine de responder, consulta [Pedir a las sesiones que hagan push de su trabajo](https://code.claude.com/docs/es/self-hosted-environments-configuration.md#prompt-sessions-to-push-their-work)
648* **Un repositorio agregado a mitad de sesión puede fallar al clonarse**: Claude lo clona con `git clone` a través de HTTPS. En un runner sin [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), la clonación falla con un error de autenticación de git si nada en el host puede leer el repositorio. Siempre que puedas, selecciona todos los repositorios que la sesión necesita al crearla.771* **Un repositorio agregado a mitad de sesión puede fallar al clonarse**: Claude lo clona con `git clone` a través de HTTPS. En un runner sin [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), la clonación falla con un error de autenticación de git si nada en el host puede leer el repositorio. Siempre que puedas, selecciona todos los repositorios que la sesión necesita al crearla.
649* **Algunos conectores no aparecen en sesiones autohospedadas**: un conector que aún no ha conectado en la configuración de claude.ai no se enumera en una sesión autohospedada, y la sesión no le pedirá que lo conecte. Conéctelo en Configuración primero, luego inicie una sesión nueva. Agregar un conector a una sesión ya en ejecución tampoco hace que sus herramientas estén disponibles para Claude; inicie una sesión nueva para recoger un conector recién agregado.772* **Algunos conectores no aparecen en sesiones autohospedadas**: un conector que aún no ha conectado en la configuración de claude.ai no se enumera en una sesión autohospedada, y la sesión no le pedirá que lo conecte. Conéctelo en Configuración primero, luego inicie una sesión nueva. Agregar un conector a una sesión ya en ejecución tampoco hace que sus herramientas estén disponibles para Claude; inicie una sesión nueva para recoger un conector recién agregado.
650* **Cuando alguien archiva o elimina una sesión, o libera su runner desde el menú de la sesión**: no cuentes con el push de [`--push-outcome-on-release`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags). Para guardar los commits en esos casos, haz push de `HEAD` a una rama de rescate desde un [hook `post-session`](https://code.claude.com/docs/es/self-hosted-environments-configuration#post-session). Si partes del ejemplo de esa sección, haz que haga push incluso cuando no haya nada sin commit, porque solo hace push después de un commit de instantánea.773* **Cuando alguien archiva o elimina una sesión, o libera su runner desde el menú de la sesión**: no cuentes con el push de [`--push-outcome-on-release`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags). Para guardar los commits en esos casos, haz push de `HEAD` a una rama de rescate desde un [hook `post-session`](https://code.claude.com/docs/es/self-hosted-environments-configuration#post-session). Si partes del ejemplo de esa sección, haz que haga push incluso cuando no haya nada sin commit, porque solo hace push después de un commit de instantánea.
684* **Las sesiones fallan inmediatamente después de la recogida**: abre la sesión en claude.ai/code para ver el error. Si el error muestra que git no pudo autenticarse, revisa las [credenciales de git](#configure-git) del runner. En un runner iniciado con `--use-anthropic-git-proxy`, consulta [Cuando las sesiones no se inician en un runner con el proxy de git](#when-anthropic-doesnt-serve-a-session).807* **Las sesiones fallan inmediatamente después de la recogida**: abre la sesión en claude.ai/code para ver el error. Si el error muestra que git no pudo autenticarse, revisa las [credenciales de git](#configure-git) del runner. En un runner iniciado con `--use-anthropic-git-proxy`, consulta [Cuando las sesiones no se inician en un runner con el proxy de git](#when-anthropic-doesnt-serve-a-session).
685 808
686 Un directorio base no escribible detiene el runner al inicio en lugar de hacer fallar las sesiones. Consulta la entrada **El runner se cierra al inicio con `cannot create or write to base directory`** en esta lista.809 Un directorio base no escribible detiene el runner al inicio en lugar de hacer fallar las sesiones. Consulta la entrada **El runner se cierra al inicio con `cannot create or write to base directory`** en esta lista.
810* **Una sesión se recoge, pero Claude nunca responde su primer mensaje**: si tus runners llegan a `api.anthropic.com` a través de un proxy, configúralo para que deje pasar la respuesta `/worker/events/stream` de la sesión a medida que llega. Aumentar los tiempos de espera del runner no ayuda. Para confirmar la causa en el registro del runner, consulta [Una sesión se recoge, pero Claude nunca responde su primer mensaje](#when-a-sessions-first-message-is-never-answered).
687* **Las sesiones no se inician en un runner que estableció `--use-anthropic-git-proxy`**: busca en el registro del runner `access denied by the git proxy`, o un error de git que nombre una dirección de `api.anthropic.com` que contenga `/git_proxy/`. Para saber si Anthropic atendió la sesión y corregir la causa, consulta [Cuando las sesiones no se inician en un runner con el proxy de git](#when-anthropic-doesnt-serve-a-session).811* **Las sesiones no se inician en un runner que estableció `--use-anthropic-git-proxy`**: busca en el registro del runner `access denied by the git proxy`, o un error de git que nombre una dirección de `api.anthropic.com` que contenga `/git_proxy/`. Para saber si Anthropic atendió la sesión y corregir la causa, consulta [Cuando las sesiones no se inician en un runner con el proxy de git](#when-anthropic-doesnt-serve-a-session).
688* **Las sesiones no pueden alcanzar la red a través de un proxy de salida autenticador**: cuando la fuente que estableció con [`--proxy-authorization-command` o `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) falla, agota el tiempo de espera después de 30 segundos o produce un valor vacío, el runner responde esa conexión con `502 Bad Gateway` y registra por qué. El runner redacta stderr del comando en ese registro y nunca registra el valor del encabezado. Con `--proxy-authorization-command`, ejecute el comando usted mismo en el host para confirmar que imprime el valor de encabezado completo en stdout. Si el runner se cierra al inicio con `could not start the proxy-authorization listener`, no pudo abrir su oyente de loopback.812* **Las sesiones no pueden alcanzar la red a través de un proxy de salida autenticador**: cuando la fuente que estableció con [`--proxy-authorization-command` o `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) falla, agota el tiempo de espera después de 30 segundos o produce un valor vacío, el runner responde esa conexión con `502 Bad Gateway` y registra por qué. El runner redacta stderr del comando en ese registro y nunca registra el valor del encabezado. Con `--proxy-authorization-command`, ejecute el comando usted mismo en el host para confirmar que imprime el valor de encabezado completo en stdout. Si el runner se cierra al inicio con `could not start the proxy-authorization listener`, no pudo abrir su oyente de loopback.
689* **El runner registra líneas `Poll failed` que contienen `rejecting the malformed poll response`**: el runner recibió una respuesta de sondeo de trabajo cuyo cuerpo no es el JSON esperado de la cola. Una causa es un proxy interceptor o un portal cautivo que responde con su propia página. Si el runner se conecta a través de un proxy, configura el proxy para que pase las respuestas de `api.anthropic.com` sin alterarlas.813* **El runner registra líneas `Poll failed` que contienen `rejecting the malformed poll response`**: el runner recibió una respuesta de sondeo de trabajo cuyo cuerpo no es el JSON esperado de la cola. Una causa es un proxy interceptor o un portal cautivo que responde con su propia página. Si el runner se conecta a través de un proxy, configura el proxy para que pase las respuestas de `api.anthropic.com` sin alterarlas.
708* **Una sesión no se inicia con `This runner couldn't read`**: el runner le pidió al host de git un repositorio con sus [credenciales de git](#configure-git) y obtuvo una respuesta de no encontrado. El repositorio no existe, o esas credenciales no pueden acceder al repositorio.832* **Una sesión no se inicia con `This runner couldn't read`**: el runner le pidió al host de git un repositorio con sus [credenciales de git](#configure-git) y obtuvo una respuesta de no encontrado. El repositorio no existe, o esas credenciales no pueden acceder al repositorio.
709 * **Si administras el runner**: otorga a sus credenciales de git acceso de lectura al repositorio y luego inicia una nueva sesión.833 * **Si administras el runner**: otorga a sus credenciales de git acceso de lectura al repositorio y luego inicia una nueva sesión.
710 * **Si iniciaste la sesión**: si el repositorio no existe, inicia una nueva sesión con el repositorio correcto. Si el runner no puede acceder al repositorio, pide al administrador del runner que otorgue acceso y luego inicia una nueva sesión.834 * **Si iniciaste la sesión**: si el repositorio no existe, inicia una nueva sesión con el repositorio correcto. Si el runner no puede acceder al repositorio, pide al administrador del runner que otorgue acceso y luego inicia una nueva sesión.
711* **Las sesiones tardan minutos en iniciarse**: el clon inicial generalmente domina. Observe la [métrica](https://code.claude.com/docs/es/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_session_init_duration_seconds` para confirmar, y corte el clon con un [pre-warmed checkout](#reuse-a-pre-warmed-checkout) o un `CLAUDE_RUNNER_FETCH_DEPTH` más pequeño.835* **Las sesiones tardan minutos en iniciarse o reanudarse**: una causa es un clon desde cero. En una flota que inicia cada runner sobre un disco vacío, sin [hook `checkout`](https://code.claude.com/docs/es/self-hosted-environments-configuration#checkout), el runner clona de nuevo los repositorios de la sesión antes de iniciar Claude Code. Una sesión que se reanuda sobre un disco vacío vuelve a esperar el mismo clon.
836
837 Para evitar el clon desde cero, proporciona un [checkout precalentado](#reuse-a-pre-warmed-checkout). Si ya proporcionas uno, [confirma que el runner lo usa](#confirm-that-the-runner-uses-your-clone). Cuando el runner hace el clon por sí mismo y se cierra después de una sola sesión, reduce [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/es/self-hosted-environments-reference#environment-variable-only-settings) desde su valor predeterminado de 50 commits para obtener menos historial.
838
839 Si usas un hook, este se ejecuta en lugar del clon del runner, así que revisa lo que hace tu hook. Observa la [métrica](https://code.claude.com/docs/es/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_session_init_duration_seconds` para ver cuánto tardan los inicios.
712* **Los turnos fallan con un 401**: cuando un turno termina con un 401 o 403 de la API de Anthropic, el runner obtiene un [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/es/self-hosted-environments-configuration#wrapper-scripts) nuevo de Anthropic y lo pasa a la sesión. El turno fallido no se reintenta. Este token es de corta duración, y el runner lo rota a través del stdin de la sesión.840* **Los turnos fallan con un 401**: cuando un turno termina con un 401 o 403 de la API de Anthropic, el runner obtiene un [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/es/self-hosted-environments-configuration#wrapper-scripts) nuevo de Anthropic y lo pasa a la sesión. El turno fallido no se reintenta. Este token es de corta duración, y el runner lo rota a través del stdin de la sesión.
713 841
714 Cuando una obtención falla, el runner registra una línea `inference_token refresh failed` que dice cuándo reintentará, y continúa reintentando mientras la sesión se ejecute.842 Cuando una obtención falla, el runner registra una línea `inference_token refresh failed` que dice cuándo reintentará, y continúa reintentando mientras la sesión se ejecute.
788 916
789Para un diagnóstico guiado en el host del runner, ejecute el [subcomando doctor](#troubleshooting).917Para un diagnóstico guiado en el host del runner, ejecute el [subcomando doctor](#troubleshooting).
790 918
919### Una sesión se recoge, pero Claude nunca responde su primer mensaje
920
921Si un runner recoge una sesión pero Claude nunca responde el primer mensaje, y tus runners llegan a `api.anthropic.com` a través de un proxy, el proxy puede ser la causa. Anthropic envía a una sesión sus mensajes mediante una única respuesta en streaming que permanece abierta. Si tu proxy la almacena en búfer, o la termina antes de transmitir algo, el primer mensaje nunca llega. Las demás solicitudes de la sesión siguen funcionando.
922
923Para corregirlo, cambia cómo maneja el proxy esta solicitud:
924
925```text theme={null}
926GET https://api.anthropic.com/v1/code/sessions/<session-id>/worker/events/stream
927```
928
929* **No almacenes la respuesta en búfer**: déjala pasar a medida que llega. Su tipo de contenido es `text/event-stream`.
930* **Deja que la respuesta permanezca abierta**: puede permanecer abierta hasta una hora. Si el proxy tiene un tiempo de espera por inactividad, mantenlo muy por encima de 15 segundos, porque Anthropic envía un keep-alive al menos con esa frecuencia por defecto.
931
932Luego inicia una nueva sesión y comprueba que Claude responde. Aumentar los tiempos de espera del runner no ayuda: solo esperas más antes de que el runner detenga la sesión.
933
934#### Confirmar la causa en el registro del runner
935
936Estos pasos son para runners con Claude Code v2.1.287 o posterior, con el valor predeterminado `--log-level info`. Los runners más antiguos registran estas líneas más tarde, o no las registran.
937
9381. Espera a que transcurra [`--startup-timeout-min`](https://code.claude.com/docs/es/self-hosted-environments-reference#runner-cli-flags), 15 minutos por defecto. El runner entonces detiene la sesión y registra una línea que contiene `no child output for` y termina en `— releasing`.
9392. Busca `SDKStartup: SSE` en el registro del runner. Estas líneas siguen a una línea `finished: interrupted`, unas líneas después de la del paso 1. La mayoría no tiene marca de tiempo, así que un recolector de registros puede mostrarlas en otro lugar.
9403. Busca la línea en esta tabla:
941
942| La línea de registro contiene | Qué hacer |
943| :- | :- |
944| `SSE connect failed (no response in 30s)` o `SSE liveness timeout` | Comprueba que el proxy reenvía la solicitud, deja pasar los encabezados y los datos de la respuesta a medida que llegan, y no corta la conexión |
945| `SSE connect failed (HTTP` y un código de estado | Revisa el registro del propio proxy. Si el proxy respondió la solicitud por sí mismo, deja pasar la solicitud a través de él. |
946| `SSE connect failed (` y cualquier otro texto | El texto entre paréntesis es el error. Busca la misma conexión en el registro del propio proxy. |
947| `SSE stream ended by server`, una y otra vez | Revisa el límite de tiempo del proxy para respuestas de larga duración. Una sola línea así es normal. |
948| `SSE reconnected after` | Busca una de las otras líneas después de ella. Esta no significa que algo haya llegado a la sesión. |
949
950Si el registro no tiene ninguna de estas líneas, o el registro del proxy no muestra nada anómalo, [reporta un problema](#report-an-issue).
951
952#### Evitar que la sesión lo vuelva a intentar
953
954La sesión mantiene el mensaje pendiente, y un runner que puede atender al propietario de la sesión la recoge de nuevo. Hasta que cambies el proxy, la sesión espera de la misma manera cada vez. Para detenerlo, archiva la sesión en claude.ai/code.
955
791## Qué sigue956## Qué sigue
792 957
793* [Personalice sesiones](https://code.claude.com/docs/es/self-hosted-environments-configuration.md): scripts de envoltura, hooks de ciclo de vida, runners bajo demanda, servidores MCP, y permisos958* [Personalice sesiones](https://code.claude.com/docs/es/self-hosted-environments-configuration.md): scripts de envoltura, hooks de ciclo de vida, runners bajo demanda, servidores MCP, y permisos