Los entornos autohospedados están en beta pública en planes Team y Enterprise; Disponibilidad y limitaciones cubre la ruta de habilitación. Esta página cubre la ejecución de la flota en producción; consulte el inicio rápido para su primer runner y sesión.
Endurezca su implementación
Un runner autohospedado ejecuta código arbitrario dirigido por el modelo en su infraestructura en nombre de todos los que pueden enviar una sesión a su entorno. Eso es cualquier miembro de su organización de Anthropic, y cualquiera que pueda iniciar una sesión de canal de Claude Tag en un alcance que un Propietario enrutó al entorno. Trabaje en cada elemento antes de conectar un entorno a sistemas de producción:-
Contenedores efímeros por sesión: ejecute cada proceso de runner en un contenedor o VM nuevo que se destruya cuando el proceso salga, con
--capacity 1y el--drain-grace-sec 0predeterminado para que cada contenedor sirva exactamente una sesión. Con una capacidad más alta, o con un drenaje de gracia positivo, un contenedor sirve múltiples sesiones del mismo propietario bloqueado; consulte Ciclo de vida del runner. No reutilice un sistema de archivos entre reinicios de runner, excepto en la configuración deliberada de checkout precalentado, y nunca entre propietarios. -
Sin credenciales amplias en la imagen: no incluya claves SSH de larga duración, credenciales de proveedor de nube, o tokens de acceso personal que otorguen más de lo que una sesión necesita. Genere credenciales utilizadas durante una sesión, como tokens de push o API, por sesión desde su script de envoltura. Para el clon inicial, que ocurre antes de que se ejecute el envoltura, use un hook de ciclo de vida
checkouto--use-anthropic-git-proxy; consulte Configurar git. - Mantenga el secreto del entorno fuera de los hosts que ejecutan sesiones: el secreto del entorno puede registrar runners y recoger cualquier sesión en cola en el entorno. En una flota fija vive en cada host de runner, donde el código de cualquier sesión puede leer el archivo secreto. Prefiera runners bajo demanda, donde el secreto permanece en el host del orquestador, que nunca ejecuta código de usuario, y cada runner recibe una orden de trabajo de un solo uso que registra exactamente un runner. En una flota fija, trate el archivo de secreto del entorno como legible por cada sesión y rote el secreto después de cualquier compromiso de sesión sospechoso.
- Salida de red de negación predeterminada: restrinja el tráfico saliente del contenedor de runner y sesión en su propio límite de red en cada entorno; Salida de negación predeterminada cubre qué permitir y por qué.
- IAM de host con privilegios mínimos: la identidad de cálculo adjunta al host del runner, como un perfil de instancia o una cuenta de servicio de nodo, debe otorgar solo lo que el runner en sí necesita. Las sesiones deben obtener sus propias credenciales a través de su script de envoltura en lugar de heredar las del host.
-
Bloquee el punto final de metadatos en la nube desde las sesiones: mantener las sesiones fuera de la identidad del host requiere bloquear su acceso al punto final de metadatos en la nube, y las políticas de salida a nivel de subred no interceptan el tráfico de metadatos de enlace local, así que bloquéelo en el contenedor en sí:
- IMDSv2 con un límite de salto de uno
- GKE Workload Identity con ocultamiento de metadatos
- Una denegación explícita para
169.254.169.254en el espacio de nombres de red del contenedor de sesión
-
Aislamiento del sistema de archivos por runner: cada proceso de runner obtiene su propio directorio de trabajo que ningún otro proceso en el host puede leer o escribir. Haga
--hooks-dir, el script de envoltura, y el~/.claude/del host de solo lectura para la sesión, ya sea integrado en la imagen o montado como solo lectura. -
El envío no tiene control de acceso por entorno: cualquier miembro de su organización de Anthropic puede enviar una sesión a cualquiera de sus entornos. Si un Propietario enruta canales de Claude Tag al entorno, cualquiera que la configuración de acceso de Claude Tag admita puede iniciar sesiones de canal que se ejecuten allí. Por defecto, eso es cualquiera en el espacio de trabajo de Slack conectado, con o sin una cuenta de Claude. Trate cada host de runner como alcanzable para la ejecución de código por todos los que pueden enviar al mismo, y coloque en un host de runner solo datos y credenciales que todas esas personas pueden leer.
--lock-to-accountlimita qué sesiones de cuenta ejecuta un host determinado, pero no reduce quién puede enviar al entorno. Para hacer que los entornos autohospedados sean la única opción de selector, un Propietario puede ocultar entornos alojados por Anthropic para toda la organización desde la página Entornos en la nube. -
Aplique la protección de configuración del repositorio: elija el modo de protección con
--confine-repo-settings. Elwarnpredeterminado registra una violación y aún genera la sesión,enforcerechaza la sesión, yoffdesactiva el escaneo. El runner escanea la configuración comprometida de cada repositorio para:- Una concesión que se resuelve fuera del espacio de trabajo de esa sesión: una entrada
additionalDirectories, una reglaEdit,Write, oNotebookEditenpermissions.allow, o una entradasandbox.filesystem.allowWriteoallowRead - Un bloque
envno vacío - Una anulación de postura del operador como
sandbox.enabled: false
--trust-workspace, y no cubre hooks de repositorio,.mcp.json, o reglas de Bash; consulte Permisos y aprobación de herramientas para saber dónde pertenecen esas concesiones. - Una concesión que se resuelve fuera del espacio de trabajo de esa sesión: una entrada
La lista de permitidos de IP de su organización no cubre el tráfico de runner autohospedado por defecto. No confíe en ella como control de red para el tráfico de runner o sesión; aplique salida de negación predeterminada en su propio límite de red en su lugar, y contacte a su equipo de cuenta de Anthropic si desea aplicación de lista de permitidos de IP para su organización.
Requisitos de red
El runner y los hijos de sesión que genera hacen conexiones salientes a los hosts a continuación. Restrinja la salida del contenedor de sesión a estos hosts y los servicios internos específicos que las sesiones necesitan alcanzar; Salida de negación predeterminada cubre cómo y por qué. Estos hosts siempre son requeridos:
Si estos hosts son necesarios depende de su configuración:
El runner no alcanza
statsig.anthropic.com, *.sentry.io, claude.ai, o platform.claude.com. Estos hosts aparecen en algunas listas de verificación de red empresarial más antiguas, pero no necesita permitirlos para el tráfico de runner o sesión: las obtenciones de banderas de características van a api.anthropic.com, y el runner se autentica con el secreto del entorno en lugar de OAuth interactivo. Dos flujos del lado del host alcanzan claude.ai, así que ejecútelos desde un host cuya salida permite en lugar de ampliar la salida del contenedor de sesión: el instalador de una línea obtiene install.sh de claude.ai en el momento de la instalación, e interactivo claude auth login, que el configuración guiada, el modo firmado del doctor, y envío de CI usan, inicia sesión a través de claude.ai, claude.com, y platform.claude.com. mcp-proxy.anthropic.com tampoco es requerido: las sesiones autohospedadas no lo usan, y la entrega de los conectores de claude.ai de su organización a sesiones, cuando está habilitada para su organización, se enruta a través de api.anthropic.com. Consulte Servidores MCP.
Salida de negación predeterminada
Implemente contenedores de runner y sesión en un segmento de red o espacio de nombres cuyo tráfico saliente se limita a los hosts en la tabla de requisitos de red, su host de git, y los servicios internos específicos que las sesiones necesitan alcanzar. El producto no puede verificar o aplicar esto, así que aplíquelo en su propio límite de red en cada entorno. El código de sesión está dirigido por el modelo e intenta conexiones a hosts arbitrarios; la salida de negación predeterminada a nivel de red limita dónde esos intentos pueden aterrizar. Esto se aplica independientemente del modo de permiso: el conjunto de herramientas preaprobadas predeterminadas ya incluyeBash, así que la salida de shell se ejecuta sin un aviso incluso sin modo automático.
Para detalles sobre qué telemetría emite cada sesión y cómo desactivarla, consulte Telemetría.
Autentíquese en un proxy de salida
Algunos proxies de salida corporativos requieren un encabezadoProxy-Authorization en cada conexión. El token en ese encabezado a menudo rota demasiado rápido para escribir en la URL del proxy que establece en HTTPS_PROXY. Establezca HTTPS_PROXY o HTTP_PROXY en la URL de su proxy como de costumbre, luego establezca --proxy-authorization-command o --proxy-authorization-file para indicar al runner dónde leer el valor del encabezado. Ambas banderas requieren Claude Code v2.1.238 o posterior.
Elija de dónde viene el valor Proxy-Authorization
Elija la bandera que coincida con cómo produce el token Proxy-Authorization:
--proxy-authorization-command <command>: elija esto para un token que genera bajo demanda. El runner ejecuta el comando de shell y usa su stdout recortado como el valor del encabezado, por ejemploBearer <token>.--proxy-authorization-file <path>: elija esto para un token que otro proceso rota en su lugar. El runner lee el archivo y usa su contenido recortado como el valor del encabezado.
Configuraciones que el runner se niega a iniciar con
Cada bandera también tiene una forma de variable de entorno, listada junto a ella en la referencia de banderas CLI del runner. Antes de que el runner contacte su proxy o el plano de control, verifica las banderas y sus variables, y se niega a iniciar en tres casos:- Ambas banderas establecidas: una bandera más la variable de entorno de la otra bandera cuenta como establecer ambas.
- Sin URL de proxy: ni
HTTPS_PROXYniHTTP_PROXYcontiene una URLhttp://ohttps://. El runner lee ambas variables en mayúsculas o minúsculas, y no consultaALL_PROXY. - Cualquiera de las banderas pasadas al subcomando del orquestador:
self-hosted-runner orchestratorno acepta las banderas o sus variables de entorno. Pase la bandera a cada runner que inicia el orquestador en su lugar.
Lo que el runner cambia mientras se establece una bandera de autorización de proxy
Con cualquiera de las banderas establecidas, el runner inicia un oyente propio y envía tráfico de proxy desde sí mismo, sus hooks de ciclo de vida, y sus sesiones a través de ese oyente. El oyente agrega el encabezadoProxy-Authorization en el camino a su proxy.
- Oyente: el oyente es un proxy directo en
127.0.0.1. El runner inicia el oyente antes de registrarse con el plano de control, y sale al inicio si el oyente no puede iniciar. - Variables de proxy: el runner reescribe cualquiera de
HTTPS_PROXYeHTTP_PROXYque establezca para que apunte al oyente. Ese valor reescrito alcanza el runner en sí, sus hooks de ciclo de vida, y cada sesión que ejecuta. - Rotación de token: un token rotado entra en vigor sin un reinicio. Para cada conexión que el oyente abre a su proxy, el runner ejecuta su comando o lee su archivo nuevamente y agrega el resultado como el encabezado.
- Entorno de sesión: una sesión alcanza su proxy solo a través del oyente. En el entorno de cada sesión, el runner elimina
ALL_PROXY, elimina cualquier ortografía deHTTPS_PROXYoHTTP_PROXYque no haya establecido, y fijaNO_PROXYal valor propio del runner. - Registros: el runner nunca registra el valor del encabezado.
Configurar git
El runner gestiona checkouts de repositorio pero no configura la identidad de git o credenciales por defecto. Usted controla la imagen y el entorno de proceso del runner, así que controla la configuración de git. Elija uno de dos enfoques:- Deje que el runner configure git: inicie el runner con
--configure-gitpara que escriba la misma identidad y configuración de firma de commit que usan las sesiones alojadas por Anthropic - Envíe la configuración de git en su imagen: establezca la identidad y las credenciales de push usted mismo, por ejemplo para hacer commits bajo su propia identidad de bot
--configure-git la firma de commit SSH requiere Git 2.34 o más reciente, --use-anthropic-git-proxy requiere 2.32 o más reciente, y reanudar sesiones desde ramas empujadas por --push-outcome-on-release requiere 2.29 o más reciente. Git 2.24 es suficiente si omite los tres y gestiona la identidad de git usted mismo.
Deje que el runner configure git
Inicie el runner con--configure-git, o establezca SELF_HOSTED_RUNNER_CONFIGURE_GIT=1, para que escriba la configuración global de git al inicio:
user.name = Claudeyuser.email = noreply@anthropic.com, coincidiendo con sesiones alojadas por Anthropic- Firma de commit y etiqueta en formato SSH, enrutada a través de un shim gestionado por el runner que firma cada commit a través del servicio de firma de Anthropic usando las credenciales de la sesión. Las firmas son verificables en GitHub contra la clave de firma SSH publicada de Anthropic.
Envíe la configuración de git en su imagen
La identidad de Git es requerida para cualquier commit. Establézcala a nivel del sistema en su Dockerfile para que la configuración se aplique independientemente de qué usuario ejecute el proceso del runner:git commit falla con Please tell me who you are y las sesiones no pueden progresar. Puede usar su propia identidad de bot en su lugar; el runner no anula estos valores.
No hornee credenciales de push de larga duración o ampliamente alcanzadas en una imagen de runner compartida: una credencial en la imagen está disponible para cada sesión que ejecuta la imagen, quienquiera que la haya iniciado. En su lugar, genere un token de corta duración y alcance mínimo por sesión desde su script de envoltura, usando la identidad del creador de la sesión decodificada del JWT de la sesión. Emparéjelo con un contenedor efímero por sesión, que requiere --capacity 1, para que ninguna credencial sobreviva a la sesión que la generó; consulte la sección de endurecimiento.
Si debe configurar credenciales de push a nivel de imagen, por ejemplo para una clave de implementación de solo lectura, limítelas lo más posible que su host de git permita:
- Una clave de implementación SSH limitada a un repositorio con una reescritura
url.<base>.insteadOf - Un
credential.helperque devuelve un token de alcance mínimo GIT_SSH_COMMANDapuntando a una clave de alcance estrecho
- El runner establece
GIT_TERMINAL_PROMPT=0, por lo que git no pide un nombre de usuario o contraseña. - El runner ejecuta SSH con
BatchMode=yes, añadido a suGIT_SSH_COMMANDsi establece uno, por lo que SSH no pide una frase de contraseña o confirmación de host. - El runner establece
GCM_INTERACTIVE=never, por lo que Git Credential Manager no abre un diálogo de inicio de sesión. - El runner borra
core.askPass, así que si usa un ayudante askpass, establézcalo a través de la variable de entornoGIT_ASKPASSen su lugar.
safe.directory:
Use el proxy de git de Anthropic
Inicie el runner con--use-anthropic-git-proxy, o establezca CLAUDE_RUNNER_USE_GIT_PROXY=1, para que clone a través del proxy de git de Anthropic, autenticado con el token de corta duración de la sesión. Para sesiones de usuario ordinarias, el proxy usa el token OAuth de GitHub o GitHub Enterprise almacenado para el creador de la sesión; para sesiones de bot y agente, usa el token de instalación de GitHub App de su organización. De cualquier manera, la imagen del runner no necesita credenciales de git en absoluto: sin claves SSH, sin ayudante de credenciales, sin .netrc. Esta es la misma ruta de autenticación que usan los entornos alojados por Anthropic.
El proxy requiere --capacity 1 porque la URL del proxy es por sesión, y git 2.32 o más reciente porque git más antiguo ignora el mecanismo de configuración que el proxy usa para aislar sesiones entre sí. El runner se niega a iniciar si alguno de los requisitos no se cumple. Porque el proxy obtiene del lado de Anthropic, su host de git debe ser alcanzable desde la infraestructura de Anthropic, el mismo requisito que tienen las sesiones alojadas por Anthropic; para un host de git que solo es enrutable dentro de su red, use un hook de ciclo de vida checkout en su lugar. Cada proceso de runner maneja una sesión a la vez, así que ejecute más réplicas para paralelismo. Cuando el proxy está habilitado, --git-host-rewrite y --git-ssh-rewrite no tienen efecto: la URL del proxy apunta a api.anthropic.com, no a su host de git.
Reescriba URLs de git para redes privadas
Las URLs de repositorio llegan desde el plano de control como HTTPS, con el nombre de host de su host de git; para GitHub Enterprise, ese es el nombre de host que configuró para la integración de GitHub Enterprise en la configuración de administrador de Claude Code en claude.ai. Dos banderas repetibles reescriben esas URLs antes del clon:--git-host-rewrite <from>=<to>: para DNS de horizonte dividido, donde Anthropic alcanza su host de git a través de un nombre de host externo pero los runners deben usar uno interno--git-ssh-rewrite <host>: para hosts de git que solo aceptan SSH, reescribiendohttps://<host>/owner/repoagit@<host>:owner/repo
--git-ssh-rewrite si necesita ambos. Para control total sobre el checkout, use un hook de ciclo de vida checkout.
Construya la imagen del runner
Anthropic no publica una imagen de runner precompilada. Construya la suya alrededor del binarioclaude, agregando cualquier cadena de herramientas que sus repositorios necesiten: tiempos de ejecución de lenguaje, compiladores, gestores de paquetes, y sidecars de MCP.
Las recetas a continuación usan --capacity 4, por lo que un contenedor sirve hasta cuatro sesiones concurrentes del mismo propietario bloqueado. Eso no proporciona el aislamiento del contenedor por sesión en la sección de endurecimiento: antes de conectar un entorno a sistemas de producción, ejecute las recetas en --capacity 1 con un contenedor por sesión, o use runners bajo demanda, que también mantienen el secreto del entorno fuera de los hosts que ejecutan sesiones.
Este Dockerfile es un punto de partida mínimo:
linux-x64 por linux-arm64 si sus nodos son ARM, o por linux-x64-musl o linux-arm64-musl en una imagen basada en musl como Alpine; consulte Configuración de Alpine Linux para los paquetes adicionales que las imágenes musl necesitan. La URL es la ubicación de lanzamiento estándar de Claude Code, por lo que puede verificar el binario descargado contra el manifiesto firmado del lanzamiento como se describe en Integridad binaria y firma de código. Construya la imagen con la versión 2.1.224 de Claude Code o posterior, luego empújela a su registro y hágale referencia en las recetas a continuación:
Dimensione CPU y memoria para sesiones
Dimensione el contenedor o host de un runner para las sesiones que ejecuta en lugar de para el proceso del runner. El runner en sí sondea trabajo, prepara el checkout de cada sesión, ejecuta sus hooks de ciclo de vida, e inicia y supervisa los procesos de sesión. La carga proviene de las sesiones: cada una es un proceso de Claude Code más lo que inicia, como compilaciones, suites de prueba, instalaciones de paquetes, y servidores MCP. Para una sesión, comience con los siguientes valores, indicados como solicitudes y límites de Kubernetes o el equivalente de su plataforma, y trate los como un punto de partida en lugar de un requisito:- Memoria: una solicitud y un límite de 4 GiB cada uno, que cumple con el mínimo de 4 GB en los requisitos del sistema de Claude Code. Mantenga los dos iguales para que el programador tenga en cuenta la memoria completa del contenedor. Cuando el contenedor alcanza su límite de memoria, el kernel mata procesos dentro de él, lo que puede terminar una sesión a mitad de la tarea.
- CPU: una solicitud de 2 CPUs y un límite de 4 CPUs, para que una sesión pueda aumentar por encima de la solicitud durante compilaciones. El kernel acelera un contenedor en su límite de CPU en lugar de matar procesos en él, por lo que las sesiones en el límite se ejecutan más lentamente pero siguen ejecutándose.
resources:
--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. Lo que dar a un contenedor depende de cuántas sesiones sirve a la vez:
- 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 recomienda, y para runners bajo demanda, donde establece los valores en la carga de trabajo que su hookspawn-runnerenvía, como la plantilla de pod de un Job de Kubernetes. - Varias sesiones por runner: en un
--capacitypor 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 y Docker Compose ejecutan--capacity 4sin límites de CPU o memoria, así que agregue límites dimensionados para la capacidad que ejecuta.
Kubernetes
El runner sirveGET /healthz en el puerto 8080 por defecto, configurable con --health-port, por lo que los sondeos de Kubernetes funcionan sin configuración adicional. El punto final devuelve 200 siempre que el proceso esté vivo, por lo que los sondeos a continuación detectan un proceso muerto, no uno atascado; para detectar un runner que dejó de sondear, alerte en la serie last_poll_age_seconds de /metrics. El Deployment a continuación monta el secreto del entorno desde un Secret de Kubernetes, apunta los sondeos de vivacidad y preparación a /healthz, y establece un período de gracia de terminación de 90 segundos. Consulte Tiempo de apagado para saber por qué importa el período de gracia.
El manifiesto no establece resources de CPU o memoria en el contenedor del runner. Agregue un bloque dimensionado para la capacidad que ejecuta, como Dimensione CPU y memoria para sesiones describe.
claude-runners. Cree el espacio de nombres primero:
(umask 077 && cat > ./environment-secret), pegue el secreto, presione Enter, luego Ctrl-D. Luego cree el Secret y elimine el archivo:
Docker Compose
El servicio Compose a continuación reinicia el runner siempre que sale, lo que cubre tanto bloqueos como la salida normal después del drenaje. Una política de reinicio de Docker reinicia el mismo contenedor con su capa escribible intacta, por lo que el runner regresa en un sistema de archivos reutilizado en lugar del nuevo que la postura de endurecimiento recomienda; use esta receta para evaluación, y para producción recrear el contenedor por ejecución o use un orquestador que lo haga.Tiempo de apagado
EnSIGTERM, el runner deja de tomar trabajo nuevo y, a menos que establezca --defer-shutdown-max-min, espera hasta --drain-wait-sec, cero por defecto, para que los turnos en vuelo terminen, termina el árbol de procesos de cada sesión, y ejecuta el hook de ciclo de vida post-session. Ese árbol de procesos incluye comandos que Claude aún estaba ejecutando en la sesión.
La ruta de drenaje completa necesita hasta --session-stop-grace-sec + --drain-wait-sec + --post-session-hook-timeout-sec, más 15 segundos de sobrecarga fija para limpieza de procesos, más 30 segundos más cuando --push-outcome-on-release está establecido. Eso es 80 segundos en valores predeterminados, y el runner registra el total al inicio. Las sesiones drenan en paralelo bajo este presupuesto, por lo que el total no crece con --capacity.
En el --drain-wait-sec 0 predeterminado, un reinicio rodante interrumpe turnos en vuelo; cada sesión se reanuda en otro runner, perdiendo trabajo no empujado como se describe en Problemas conocidos. Establezca --drain-wait-sec, y aumente el período de gracia para que coincida, para dejar que los turnos terminen primero.
A lo largo de toda esa ruta, el runner sigue latiendo al plano de control con capacidad cero, por lo que el arrendamiento de sesión no expira y se reencola a otro runner mientras el hook post-session aún está escribiendo trabajo no comprometido. El latido se detiene justo antes de que el runner se desregistre.
Dé al runner al menos el total que registra al inicio antes de que el host lo detenga. Dónde establece eso depende de cómo se detienen sus hosts:
- Con un período de gracia
SIGTERM: establezcaterminationGracePeriodSecondsen Kubernetes,stop_grace_perioden Docker Compose, o el equivalente de su orquestador en al menos ese total. El valor predeterminado de Kubernetes de 30 segundos es más corto que la ruta de drenaje del runner, por lo que Kubernetes detiene el pod antes de que el runner termine de drenar. - Con
--retire-at: dimensione el margen entre el tiempo de jubilación y el tiempo de parada del host para cubrir turnos típicos, más la retención de tarea de fondo que Ciclo de vida del runner describe, más ese mismo total. Calcule el tiempo de jubilación en cada lanzamiento, por ejemplodate +%smás la vida útil prevista del runner. - Con
--defer-shutdown-max-min: agregue dos partes más al total de la ruta de drenaje. La primera es los minutos que configura. La segunda es la gracia posterior a la liberación que Diferir el drenaje más allá de la primera señal describe, 75 segundos en valores predeterminados. Con la bandera establecida, el runner también imprime la cifra combinada al inicio, después del total de la ruta de drenaje.
Diferir el drenaje más allá de la primera señal
Establezca--defer-shutdown-max-min <n> si desea que un runner que está reiniciando continúe sirviendo las sesiones que mantiene durante hasta n minutos, en lugar de drenarlas en la primera señal. En la primera SIGTERM o SIGINT, el runner deja de tomar trabajo nuevo y continúa sirviendo las sesiones que mantiene. Sigue sondeando para que el plano de control no reencole esas sesiones. Requiere Claude Code v2.1.238 o posterior.
Lo que sucede con las sesiones que el runner mantiene después de la primera señal
En las primeras dos etapas que siguen a la señal, el runner libera sesiones, y una sesión liberada se reanuda en un runner nuevo cuando su usuario envía su siguiente mensaje. Contando desde la primera señal, el runner se mueve a través de tres etapas:- Durante los primeros
nminutos: el runner sirve sus sesiones normalmente y sigue aplicando--startup-timeout-miny--kill-session-after-min. Si también establece--release-idle-session-min, el runner libera cualquier sesión cuyo usuario ha estado inactivo ese tiempo; sin ella, el runner no libera ninguna sesión temprano, aparte de un tiempo de espera de inicio. - Cuando los
nminutos se agotan: el runner libera cada sesión que aún mantiene, inactiva o no. El runner espera a que la sesión de mitad de turno termine su turno, y hasta 60 segundos más para las tareas de fondo de un turno, antes de liberar esa sesión. - Cuando la gracia posterior a la liberación se agota: el runner drena cualquier sesión que aún mantiene, y el plano de control reencola cada sesión drenada a otro runner de inmediato. La gracia posterior a la liberación comienza cuando los
nminutos se agotan y es 75 segundos en valores predeterminados. Si establece--drain-wait-secpor encima de 60 segundos, la gracia posterior a la liberación es--drain-wait-secmás 15 segundos en su lugar.
--defer-shutdown-max-min. Una vez que un drenaje está en marcha, la siguiente señal fuerza la salida del runner. Eso se mantiene si una segunda señal o la gracia posterior a la liberación que se agota inició el drenaje.
Dimensione el tiempo de parada
Dé al tiempo de parada de su host al menos la suma de tres partes: losn minutos que configura, la gracia posterior a la liberación, y la ruta de drenaje completa que Tiempo de apagado describe. Con configuraciones predeterminadas, la gracia posterior a la liberación es 75 segundos y la ruta de drenaje es 80 segundos, así que permita n minutos más 155 segundos. El runner imprime esta suma al inicio siempre que --defer-shutdown-max-min está establecido.
Si el tiempo de parada se agota antes de que el runner termine, el host mata el runner. Las sesiones que aún mantiene no obtienen ningún hook post-session. El runner no se desregistra, y el plano de control reencola las sesiones aproximadamente un minuto después. Si no puede dar al tiempo de parada esa suma, deje --defer-shutdown-max-min sin establecer para que el runner drene en la primera señal en su lugar.
Lo que alcanza un hook post-session en ejecución
El hookpost-session y el hijo de sesión de Claude cada uno se ejecutan en su propio grupo de procesos POSIX, separado del runner, por lo que los mecanismos de parada los alcanzan de manera diferente:
- Un
SIGTERMmientras el runner ya está drenando: fuerza la salida del runner inmediatamente, omitiendo lo que queda de la ruta de drenaje. Sin--defer-shutdown-max-min, eso es el segundoSIGTERMque recibe el runner. Nada señala un hookpost-sessionen ejecución, por lo que en un host desnudo donde un proceso init adopta huérfanos, termina por su cuenta, pero sin supervisión: su presupuesto de tiempo ya no se aplica, y una escritura en la tubería de registro cerrada puede matarlo conSIGPIPE, así que un hook que necesita sobrevivir a una salida forzada allí debe redirigir su propia salida a un archivo. En las recetas de contenedor en esta página el runner es el PID 1 del contenedor y su salida termina el contenedor, y bajo elKillMode=control-grouppredeterminado de systemd la matanza de cgroup alcanza el hook también, como la entrada Matanzas de cgroup describe; en ambos, trate una salida forzada como fatal para el hook y confíe en el período de gracia en su lugar. - Señales de grupo de procesos, como
kill -- -<pid>en un script de envoltura, control de trabajo de shell, o un vigilante de grupo: alcanzan el runner y un subproceso de hookcheckouten ejecución, que permanece adjunto al grupo deliberadamente, pero no un hookpost-sessionen ejecución o el hijo de sesión. - Matanzas de cgroup, como el
KillMode=control-grouppredeterminado de systemd o elSIGKILLque Kubernetes entrega a todo el contenedor cuandoterminationGracePeriodSecondsexpira: alcanzan todo, incluido el hook. El aislamiento de grupo de procesos no protege contra estos, que es por qué el período de gracia debe cubrir la ruta de drenaje completa. - El tiempo de espera propio del hook: cuando un hook excede
--post-session-hook-timeout-sec, el runner envíaSIGTERMa todo el grupo de procesos del hook, luegoSIGKILLdos segundos después, por lo que un trabajador que el hook bifurcó, como tar, rsync, o git, termina con el shell de envoltura en lugar de sobrevivir como un huérfano. La supervisión del runner termina una vez que el stdio del hook se cierra: un trabajador que redirigió su propia salida a un archivo y sobrevive a la etapaSIGTERMestá más allá del alcance del runner.
post-session aún se están ejecutando, para que pueda distinguir un drenaje tranquilo de uno que está en mitad de una instantánea.
Mantenga el directorio base y la capacidad idénticos en todos los runners
Si un runner muere en mitad de sesión, el servidor reencola la sesión y otro runner en el entorno la recoge. Ese runner deriva la ruta de checkout de su propio--base-dir y --capacity: --capacity 1 verifica directamente bajo --base-dir, y un --capacity por encima de 1 usa worktrees por sesión en su lugar. Cuando los runners en el mismo entorno usan valores diferentes para cualquiera de las banderas, el directorio de trabajo de la sesión reanudada cambia, y las rutas absolutas que el agente registró anteriormente, en ediciones, llamadas de herramientas, o sus propias notas, apuntan a una ubicación que ya no existe.
Use el mismo --base-dir y --capacity en cada runner en un entorno, y no use un valor por host como un ID de instancia o nombre de host.
El directorio base tiene como valor predeterminado /workspace, con la excepción que la fila de referencia --base-dir registra. El runner necesita acceso de escritura a él. Al inicio, antes de registrarse, el runner crea el directorio y confirma que puede escribir en él, y sale con cannot create or write to base directory cuando no puede. Un runner iniciado como root crea el /workspace predeterminado en sí. Para un runner que no es root, cree el directorio y dé al usuario del runner la propiedad antes de iniciar el runner, o apunte --base-dir a un directorio que ese usuario ya posee.
Reutilice un checkout precalentado
Para repositorios grandes, el clon puede dominar el inicio de sesión. En--capacity 1 sin hook checkout, el runner mantiene un clon canónico por repositorio en <base-dir>/<repo-owner>/<repo> y lo reutiliza en sesiones: obtiene la ref solicitada, desasocia HEAD, y reinicia duro a ella, que es casi instantáneo cuando poco ha cambiado. Para omitir el clon frío, suministre el clon de una de dos maneras:
- Clon en la imagen: construya el clon en su imagen de runner en esa ruta. Cada contenedor nuevo comienza con el clon precalentado sin reutilizar un disco.
- Clon en un volumen persistente: en runners que prebloquea a la cuenta de un usuario con
--lock-to-account, apunte--base-dira un volumen persistente, para que el disco solo sirva esa cuenta. Un runner prebloquado nunca recoge sesiones de canal de Claude Tag, por lo que esta opción no se aplica a runners que las sirven.
- Cualquier forma de clon funciona: un clon completo, superficial, o de rama única en la ruta se usa tal cual. El runner nunca pasa
--depthcuando 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. - Los cambios rastreados se reinician, los archivos sin rastrear persisten: 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. - Con el proxy de git, el reinicio se convierte en un checkout: con
--use-anthropic-git-proxy, el runner sanitiza el.git/del clon antes de cada sesión, manteniendo el almacén de objetos, refs, y estado superficial pero eliminando el índice, por lo que cada sesión paga un checkout de árbol de trabajo completo en lugar de un reinicio casi instantáneo; aún nunca vuelve a clonar. Los precalentamientos de submódulos no son compatibles con el proxy. - 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.
Fije la versión
El proceso hijo de Claude Code 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, por lo que cada sesión ejecuta la versión que instaló en el host o construyó en la imagen. Una actualización a nivel de host entra en vigor la próxima vez que el runner comienza.- Para mantener una flota en una versión: construya la imagen con una versión fijada, o en un host desnudo instale una versión específica y desactive las actualizaciones automáticas
- Para actualizar: instale la versión más reciente o reconstruya la imagen, luego reinicie los runners
- Plugins: los mercados de plugins tampoco se actualizan automáticamente; establezca
FORCE_AUTOUPDATE_PLUGINS=1en el entorno del runner para permitir que los plugins se actualicen automáticamente mientras el binario permanece fijado
Escale la flota
Su orquestador decide cuándo agregar o eliminar runners. Debido al bloqueo de un propietario por runner, el recuento de réplicas mínimo es el número de usuarios y agentes de Claude Tag que espera que estén activos simultáneamente;--capacity controla el paralelismo dentro de las sesiones de un propietario, no entre propietarios.
Dos enfoques de escalado están disponibles:
- Flota fija: ejecute un conjunto estático de réplicas de runner y escale en las métricas de Prometheus que cada runner sirve
- 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 hookspawn-runnerpara arrancar uno por sesión. Consulte Runners bajo demanda.
Problemas conocidos y limitaciones
Las siguientes son las limitaciones en esta versión, con soluciones alternativas donde exista una.El tráfico del conector sale de su red
Anthropic llama herramientas de conector, como GitHub, Slack, Linear, y los otros conectores de claude.ai, desde su propia infraestructura en lugar de desde su runner, por lo que cuando Claude usa un conector en una sesión autohospedada, ese tráfico va a través deapi.anthropic.com en lugar de originarse dentro de su límite de red. Para mantener un conector fuera de sesiones autohospedadas, filtrelo como cualquier otro servidor MCP con la configuración de política allowedMcpServers y deniedMcpServers. Claude Code aplica estas configuraciones a los conectores que Anthropic entrega así como a los servidores que configura, por lo que si implementa una lista de permitidos para otros servidores, Claude Code bloquea conectores entregados también. Para mantener conectores disponibles junto con una lista de permitidos basada en URL, agregue entradas que coincidan con las rutas de proxy de Anthropic para conectores entregados:
https://api.anthropic.com/v2/ccr-sessions/*https://api.anthropic.com/v1/code/sessions/*https://api.anthropic.com/v1/code/mcp/*
Algunas sesiones no cuentan como inactivas
Una sesión que mantiene una tarea de fondo que nunca termina no cuenta como inactiva, por lo que--release-idle-session-min no liberará la ranura de esa sesión. Una sesión que espera una aprobación solicitada desde dentro de una llamada de herramienta en ejecución tampoco cuenta como inactiva. Siempre establezca --kill-session-after-min junto con ella como un tope duro para que ninguna sesión pueda mantener una ranura indefinidamente.
--kill-session-after-min es un tope para sesiones descontroladas. El runner termina cualquier sesión que alcance el límite, incluso una que alguien aún está usando, así que establezca la bandera bien por encima de su sesión más larga esperada, como --kill-session-after-min 480 para 8 horas. Para liberar ranuras de conversaciones que se vuelven inactivas, use --release-idle-session-min en su lugar.
Limitaciones adicionales
- Las sesiones reanudadas pierden trabajo no empujado: cuando una sesión se libera, en tiempo de espera inactivo o en un reinicio de runner, y el usuario envía otro mensaje, la sesión se reanuda en un runner nuevo que clona el repositorio nuevamente desde su rama inicial, por lo que el trabajo que la sesión no había empujado se ha ido. Establezca
--push-outcome-on-releasepara que el runner haga un mejor esfuerzo para empujar las ramas de resultado de la sesión antes de liberarla, para que la sesión reanudada comience desde esos commits en su lugar; esto preserva trabajo comprometido, no un árbol de trabajo sucio. Antes de habilitarlo, restrinja quién puede empujar a refsclaude/*en el remoto de origen, por ejemplo con un conjunto de reglas de rama: en la reanudación, el runner obtiene la rama previamente empujada sin verificar quién la empujó, por lo que cualquiera con acceso de push a esos refs puede colocar contenido en el espacio de trabajo reanudado. El runner también descarta la configuración por sesión en la reanudación, lo que significa el directorio de configuración de Claude de la sesión y cualquier estado de shell que la sesión escribió;--push-outcome-on-releaseno cubre esos. - Los repositorios privados no se pueden agregar en mitad de sesión: un repositorio agregado a una sesión después de que ha comenzado no se clona con credenciales en un runner autohospedado, por lo que la adición falla. Seleccione cada repositorio que la sesión necesita cuando la crea.
- 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.
Reportar un problema
Para problemas con entornos autohospedados, contacte a su equipo de cuenta de Anthropic.Solución de problemas
Para un diagnóstico guiado, ejecute el subcomando doctor en el host del runner. El subcomando doctor inicia una sesión interactiva de Claude Code con los registros y el estado del runner adjuntos. Inicie sesión conclaude auth login en ese host primero para que la sesión pueda consultar su entorno, sus runners y sus sesiones en cola. Sin ese inicio de sesión, por ejemplo cuando el host se autentica con una clave API, se limita al punto final de salud local, las métricas y el registro del runner, y lee el registro solo si inició el runner con --log-file.
- El runner no aparece en el entorno: confirme que el host pueda alcanzar
api.anthropic.comsobre HTTPS, que el secreto del entorno sea actual y que el reloj del host esté dentro de cinco minutos de la hora real; un sesgo mayor causa que la autenticación falle. El runner registra[runner:fatal]con el motivo del rechazo en caso de fallo de autenticación. - El runner se cierra al inicio con
cannot create or write to base directory: el runner no puede crear ni escribir en--base-dir, que por defecto es/workspace. Corrija la propiedad del directorio o apunte--base-dira una ruta escribible, como se describe en Mantener el directorio base y la capacidad idénticos en todos los runners. Si el runner registra[runner:fatal]diciendo que la verificación del directorio base agotó el tiempo de espera, el directorio está en un montaje NFS o CSI colgado. Verifique la salud del montaje en lugar de los permisos. El runner imprime ambas fallas de inicio en stderr antes de abrir--log-file, así que búsquelas en la terminal o en los registros del contenedor de su plataforma en lugar del archivo de registro. Antes de v2.1.225, el runner no verificaba el directorio base al inicio, y esta configuración incorrecta fallaba en las sesiones después de la recogida. - Las sesiones permanecen en cola: cada runner en línea puede estar bloqueado a un propietario diferente. Verifique la métrica
claude_code_self_hosted_runner_locked_accountde cada runner o el campolocked_accountde su línea de registro[runner:health]para ver quién la mantiene. Ambos muestran el correo electrónico del propietario solo después de que el runner haya recibido un token de sesión que lleve un reclamoact.email, que las sesiones de un agente Claude Tag nunca hacen. Sin el reclamo, el runner no emite ninguna serielocked_accounty registralocked_account=yes, lo que le indica que el runner está bloqueado pero no a qué propietario. Agregue réplicas o espere a que un runner existente se drene y reinicie. Si el entorno usa runners bajo demanda, verifique el orquestador en su lugar; consulte On-demand runners. - Las sesiones fallan inmediatamente después de la recogida: abra la sesión en claude.ai/code para ver el error. Las causas más comunes son las credenciales de git faltantes en la imagen del runner y las herramientas de compilación que no están instaladas. Un directorio base no escribible detiene el runner al inicio en lugar de fallar en las sesiones. Consulte la entrada El runner se cierra al inicio con
cannot create or write to base directoryen esta lista. - Las sesiones no pueden alcanzar la red a través de un proxy de salida autenticador: cuando la fuente que estableció con
--proxy-authorization-commando--proxy-authorization-filefalla, agota el tiempo de espera después de 30 segundos o produce un valor vacío, el runner responde esa conexión con502 Bad Gatewayy 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 concould not start the proxy-authorization listener, no pudo abrir su oyente de loopback. - El runner registra líneas
Poll failedque contienenrejecting the malformed poll response: el runner recibió una respuesta de sondeo de trabajo cuyo cuerpo no es el JSON esperado de la cola, la mayoría de las veces porque algo entre el runner yapi.anthropic.com, como un proxy interceptor o un portal cautivo, respondió con su propia página. El runner rechaza la respuesta, la cuenta bajo el tipotransportde la métricaclaude_code_self_hosted_runner_poll_errors_total, y reintenta en el cronograma de sondeo fallido descrito en Session lifecycle. El runner continúa sirviendo sus sesiones activas. Configure el proxy para pasar las respuestas deapi.anthropic.comsin alterar. Antes de v2.1.246, el runner leía tal respuesta como una cola de trabajo vacía, lo que podría terminar sus sesiones activas o hacer que se cierre. - La rama de una sesión ya no existe en el remoto: para una fuente de git que la sesión solo lee, el runner omite esa fuente y continúa con las restantes. Para la fuente a la que la sesión envía resultados, una rama eliminada, típicamente porque fue fusionada y auto-eliminada, falla la sesión con un error que nombra el repositorio y la rama y le pide que restaure la rama y reintente. El runner falla la sesión con el mismo error cuando omitir dejaría sin repositorio en absoluto. Antes de v2.1.228, tal sesión comenzaba en un directorio vacío.
- Las sesiones tardan minutos en iniciarse: el clon inicial generalmente domina. Observe la métrica
claude_code_self_hosted_runner_session_init_duration_secondspara confirmar, y corte el clon con un pre-warmed checkout o unCLAUDE_RUNNER_FETCH_DEPTHmás pequeño. - El pod se mata a mitad del drenaje: aumente
terminationGracePeriodSecondsal menos al valor que el runner registra al inicio. Consulte Shutdown timing.
[runner:fatal], en stdout, y la salida de depuración en stderr, todo como líneas de texto sin formato en lugar de JSON. Las fallas de inicio descritas en las entradas de solución de problemas anteriores se imprimen en stderr antes de ese punto. Capture ambas secuencias con --log-file, que también permite que self-hosted-runner doctor las siga, o con la recopilación de registros de su plataforma. El proceso secundario de cada sesión escribe un registro de depuración separado. En caso de fallo, el runner preserva el registro, imprime la ruta del registro en el registro del runner y muestra la cola del registro junto con la sesión en claude.ai/code.
Qué sigue
- Personalice sesiones: scripts de envoltura, hooks de ciclo de vida, runners bajo demanda, servidores MCP, y permisos
- Pruebe de extremo a extremo: verifique una nueva imagen de runner desde CI antes de promoverla
- Referencia: cada bandera CLI, variable de entorno, y métrica