Les environnements auto-hébergés sont en bêta publique sur les plans Team et Enterprise ; Disponibilité et limitations couvre le chemin d’activation. Cette page couvre l’exécution de la flotte en production ; consultez le guide de démarrage rapide pour votre premier runner et session.
Sécurisez votre déploiement
Un runner auto-hébergé exécute du code arbitraire, dirigé par le modèle, sur votre infrastructure au nom de tous ceux qui peuvent dispatcher une session vers son environnement. Il s’agit de tout membre de votre organisation Anthropic, et de toute personne qui peut démarrer une session de canal Claude Tag dans une portée qu’un propriétaire a routée vers l’environnement. Travaillez sur chaque élément avant de connecter un environnement à des systèmes de production :-
Conteneurs éphémères, par session : exécutez chaque processus runner dans un conteneur ou une VM fraîche qui est détruite lorsque le processus se termine, avec
--capacity 1et la valeur par défaut--drain-grace-sec 0afin que chaque conteneur serve exactement une session. À une capacité plus élevée, ou avec une période de drainage positive, un conteneur sert plusieurs sessions du même propriétaire verrouillé ; voir Cycle de vie du runner. Ne réutilisez pas un système de fichiers entre les redémarrages du runner, sauf dans la configuration délibérée checkout pré-chauffé, et jamais entre les propriétaires.- Lorsque le runner arrête une session, il n’envoie aucun signal à un processus encore en cours d’exécution après la fin de sa commande shell, comme un service passé en mode daemon. La destruction du conteneur ou de la VM met fin à ce processus.
-
Pas de larges identifiants dans l’image : n’incluez pas de clés SSH de longue durée, d’identifiants de fournisseur cloud, ou de jetons d’accès personnel qui accordent plus que ce qu’une session a besoin. Générez les identifiants utilisés pendant une session, tels que les jetons push ou API, par session à partir de votre script wrapper. Le clone initial se produit avant l’exécution du wrapper ; gérez-le donc avec un hook de cycle de vie
checkout, ou avec--use-anthropic-git-proxylorsque tous les dépôts d’une session se trouvent sur github.com. Pour ces deux options, voir Configurer git. -
Tenez les identifiants GitHub de l’hôte à l’écart des sessions : Claude peut utiliser tout identifiant GitHub qu’une session peut lire, avec l’accès que cet identifiant accorde. Tenez les identifiants GitHub à portée étendue propres à l’hôte runner hors de tout ce qu’une session peut lire. Un tel identifiant peut être un jeton d’accès personnel, le jeton que
gh auth loginenregistre pour votre compte, ou unGH_TOKENdans l’environnement du runner.- Avec git géré par Anthropic : avec un tel identifiant, Claude atteint GitHub directement au lieu de passer par git géré par Anthropic.
- Sans git géré par Anthropic : un identifiant de clone peut rester dans l’image si vous limitez sa portée aussi strictement que le décrit Intégrer la configuration git dans votre image.
- Gardez le secret de l’environnement loin des hôtes exécutant les sessions : le secret de l’environnement peut enregistrer des runners et récupérer toute session mise en file d’attente sur l’environnement. Sur une flotte fixe, il réside sur chaque hôte runner, où le code de toute session peut lire le fichier secret. Préférez les runners à la demande, où le secret reste sur l’hôte orchestrateur, qui n’exécute jamais de code utilisateur, et chaque runner reçoit un bon de travail à usage unique qui enregistre exactement un runner. Sur une flotte fixe, traitez le fichier secret-environnement comme lisible par toute session et faites tourner le secret après tout compromis de session suspecté.
- Sortie réseau par défaut-refuser : limitez le trafic sortant du conteneur runner et session à votre propre limite réseau sur chaque environnement ; Sortie par défaut-refuser couvre ce qu’il faut autoriser et pourquoi.
- IAM hôte avec privilèges minimaux : l’identité de calcul attachée à l’hôte runner, telle qu’un profil d’instance ou un compte de service de nœud, ne devrait accorder que ce dont le runner lui-même a besoin. Les sessions devraient obtenir leurs propres identifiants via votre script wrapper plutôt que d’hériter de ceux de l’hôte.
-
Bloquez le point de terminaison des métadonnées cloud des sessions : garder les sessions hors de l’identité hôte nécessite de bloquer leur accès au point de terminaison des métadonnées, et les politiques de sortie au niveau du sous-réseau n’interceptent pas le trafic de métadonnées link-local, donc bloquez-le dans le conteneur lui-même :
- IMDSv2 avec une limite de saut d’un
- GKE Workload Identity avec dissimulation des métadonnées
- Un refus explicite pour
169.254.169.254dans l’espace de noms réseau du conteneur de session
-
Isolation du système de fichiers par runner : chaque processus runner obtient son propre répertoire de travail qu’aucun autre processus sur l’hôte ne peut lire ou écrire. Rendez
--hooks-dir, le script wrapper, et le~/.claude/de l’hôte en lecture seule pour la session, soit intégrés dans l’image, soit montés en lecture seule. -
Dispatch n’a pas de contrôle d’accès par environnement : tout membre de votre organisation Anthropic peut dispatcher une session vers n’importe lequel de ses environnements. Si un propriétaire route les canaux Claude Tag vers l’environnement, toute personne que le paramètre d’accès Claude Tag admet peut démarrer des sessions de canal qui s’y exécutent. Par défaut, il s’agit de toute personne dans l’espace de travail Slack connecté, avec ou sans compte Claude. Traitez chaque hôte runner comme accessible pour l’exécution de code par tous ceux qui peuvent dispatcher vers lui, et placez sur un hôte runner uniquement les données et identifiants que toutes ces personnes sont autorisées à lire.
--lock-to-accountlimite les sessions de quel compte un hôte donné exécute, mais cela ne réduit pas qui peut dispatcher dans l’environnement. Pour faire des environnements auto-hébergés la seule option de sélection, un propriétaire peut masquer les environnements hébergés par Anthropic pour toute l’organisation à partir de la page Environnements cloud. -
Appliquez la garde des paramètres de dépôt : choisissez le mode de garde avec
--confine-repo-settings. La valeur par défautwarnenregistre une violation et lance quand même la session,enforcerefuse la session, etoffdésactive l’analyse. Le runner analyse les paramètres validés de chaque dépôt pour :- Une autorisation qui se résout en dehors de l’espace de travail propre de cette session : une entrée
additionalDirectories, une règleEdit,Write, ouNotebookEditdanspermissions.allow, ou une entréesandbox.filesystem.allowWriteouallowRead - Un bloc
envnon vide - Un remplacement de posture d’opérateur tel que
sandbox.enabled: false
--trust-workspace, et ne couvre pas les hooks de dépôt,.mcp.json, ou les règles Bash ; voir Permissions et approbation des outils pour savoir où ces autorisations doivent se trouver. - Une autorisation qui se résout en dehors de l’espace de travail propre de cette session : une entrée
Si votre organisation a activé la liste d’autorisation IP, ajoutez les adresses de sortie publiques de vos runners et conteneurs de session à la liste d’autorisation avant de les démarrer. Si vous exécutez des runners à la demande, ajoutez également l’adresse de l’hôte orchestrateur. Ne vous fiez pas à la liste d’autorisation comme contrôle réseau pour le trafic du runner ou de la session. Appliquez plutôt une sortie par défaut-refuser à votre propre limite réseau.
Exigences réseau
Le runner et les enfants de session qu’il génère établissent des connexions sortantes vers les hôtes ci-dessous. Limitez la sortie du conteneur de session à ces hôtes et aux services internes spécifiques que les sessions doivent atteindre ; Sortie par défaut-refuser couvre comment et pourquoi. Ces hôtes sont toujours requis :
Un runner qui utilise
--use-anthropic-git-proxy route son trafic git github.com via api.anthropic.com, il n’a donc pas besoin du chemin vers l’hôte git pour github.com. Il a toutefois besoin de ce chemin si vous définissez --push-outcome-on-release ou si vous effectuez un push depuis un hook post-session.
Que ces hôtes soient nécessaires dépend de votre configuration :
Vous n’avez pas besoin d’ajouter ces hôtes à la liste d’autorisation pour le trafic du runner ou de la session :
statsig.anthropic.com,*.sentry.io,claude.aietplatform.claude.com: ces hôtes apparaissent dans certaines listes de contrôle réseau d’entreprise plus anciennes, mais le runner ne les atteint pas. Les récupérations de feature flags vont àapi.anthropic.com, et le runner s’authentifie avec le secret de l’environnement plutôt qu’avec OAuth interactif.mcp-proxy.anthropic.com: les sessions auto-hébergées ne l’utilisent pas. Quand la livraison des connecteurs est activée pour votre organisation, les connecteurs claude.ai de votre organisation atteignent les sessions viaapi.anthropic.com. Consultez Serveurs MCP.
claude.ai, donc exécutez-les à partir d’un hôte dont la sortie le permet plutôt que d’élargir la sortie du conteneur de session :
- L’installateur d’une ligne : récupère
install.shdepuisclaude.aiau moment de l’installation. claude auth logininteractif : se connecte viaclaude.ai,claude.cometplatform.claude.com. La configuration guidée, le mode connecté dedoctoret le dispatch CI l’utilisent. Le navigateur avec lequel vous vous connectez charge également les vérifications de navigateur de la page de connexion claude.ai depuishcaptcha.com,*.hcaptcha.cometchallenges.cloudflare.com. Si votre organisation utilise l’authentification unique (SSO), ce navigateur peut également être redirigé viasso.claude.com.
Sortie par défaut-refuser
Déployez les conteneurs runner et session dans un segment réseau ou un espace de noms dont le trafic sortant est limité aux hôtes du tableau des exigences réseau, votre hôte git, et les services internes spécifiques que les sessions doivent atteindre. Le produit ne peut pas vérifier ou appliquer cela, donc appliquez-le à votre propre limite réseau sur chaque environnement. Le code de session est dirigé par le modèle et peut tenter des connexions vers des hôtes arbitraires ; la sortie par défaut-refuser au niveau réseau limite où ces tentatives peuvent atterrir. Cela s’applique indépendamment du mode de permission : l’ensemble d’outils pré-approuvé par défaut inclut déjàBash, donc la sortie shell s’exécute sans invite même sans mode auto.
Pour plus de détails sur la télémétrie que chaque session émet et comment la désactiver, consultez Télémétrie.
S’authentifier auprès d’un proxy de sortie
Certains proxies de sortie d’entreprise nécessitent un en-têteProxy-Authorization sur chaque connexion. Le jeton dans cet en-tête tourne souvent trop vite pour être écrit dans l’URL du proxy que vous définissez dans HTTPS_PROXY. Définissez HTTPS_PROXY ou HTTP_PROXY sur l’URL de votre proxy comme d’habitude, puis définissez --proxy-authorization-command ou --proxy-authorization-file pour dire au runner où lire la valeur de l’en-tête. Les deux drapeaux nécessitent Claude Code v2.1.238 ou plus récent.
Choisir d’où provient la valeur Proxy-Authorization
Choisissez le drapeau qui correspond à la façon dont vous produisez le jeton Proxy-Authorization :
--proxy-authorization-command <command>: choisissez ceci pour un jeton que vous générez à la demande. Le runner exécute la commande shell et utilise sa stdout rognée comme valeur d’en-tête, par exempleBearer <token>.--proxy-authorization-file <path>: choisissez ceci pour un jeton qu’un autre processus fait tourner en place. Le runner lit le fichier et utilise son contenu rogné comme valeur d’en-tête.
Configurations que le runner refuse de démarrer avec
Chaque drapeau a également une forme de variable d’environnement, listée à côté dans la référence des drapeaux CLI du runner. Avant que le runner ne contacte votre proxy ou le plan de contrôle, il vérifie les drapeaux et leurs variables, et refuse de démarrer dans trois cas :- Les deux drapeaux définis : un drapeau plus la variable d’environnement de l’autre drapeau compte comme la définition des deux.
- Pas d’URL de proxy : ni
HTTPS_PROXYniHTTP_PROXYne contient une URLhttp://ouhttps://. Le runner lit les deux variables en majuscules ou minuscules, et ne consulte pasALL_PROXY. - L’un ou l’autre drapeau passé à la sous-commande orchestrateur :
self-hosted-runner orchestratorn’accepte pas les drapeaux ou leurs variables d’environnement. Passez le drapeau à chaque runner que l’orchestrateur démarre à la place.
Ce que le runner change quand un drapeau d’autorisation de proxy est défini
Avec l’un ou l’autre drapeau défini, le runner démarre son propre écouteur et envoie le trafic proxy de lui-même, ses hooks de cycle de vie, et ses sessions via cet écouteur. L’écouteur ajoute l’en-têteProxy-Authorization en route vers votre proxy.
- Écouteur : l’écouteur est un proxy avant sur
127.0.0.1. Le runner démarre l’écouteur avant de s’enregistrer auprès du plan de contrôle, et quitte au démarrage si l’écouteur ne peut pas démarrer. - Variables de proxy : le runner réécrit lequel de
HTTPS_PROXYetHTTP_PROXYvous avez défini pour qu’il pointe vers l’écouteur. Cette valeur réécrite atteint le runner lui-même, ses hooks de cycle de vie, et chaque session qu’il exécute. - Rotation de jeton : un jeton pivoté prend effet sans redémarrage. Pour chaque connexion que l’écouteur ouvre vers votre proxy, le runner exécute votre commande ou relit votre fichier et ajoute le résultat comme en-tête.
- Environnement de session : une session atteint votre proxy uniquement via l’écouteur. Dans l’environnement de chaque session, le runner supprime
ALL_PROXY, supprime toute orthographe deHTTPS_PROXYouHTTP_PROXYque vous n’avez pas définie, et épingleNO_PROXYà la valeur du runner. - Journaux : le runner ne journalise jamais la valeur de l’en-tête.
Configurer git
Le runner gère les checkouts de référentiel mais ne configure pas l’identité git ou les identifiants par défaut. Vous contrôlez l’image et l’environnement de processus du runner, donc vous contrôlez la configuration git. Choisissez l’une de deux approches :- Laisser le runner configurer git : démarrez le runner avec
--configure-gitpour qu’il écrive la même identité et configuration de signature de commit que les sessions hébergées par Anthropic utilisent - Livrer la configuration git dans votre image : définissez l’identité et les identifiants push vous-même, par exemple pour committer sous votre propre identité de bot
--use-anthropic-git-proxy, ou définir CLAUDE_RUNNER_USE_GIT_PROXY=1, pour demander à Anthropic de servir git pour les sessions du runner.
Planchers de version Git sur l’hôte runner : --configure-git la signature de commit SSH nécessite Git 2.34 ou plus récent, --use-anthropic-git-proxy nécessite 2.32 ou plus récent, et reprendre les sessions à partir de branches poussées par --push-outcome-on-release nécessite 2.29 ou plus récent. Git 2.24 est suffisant si vous omettez les trois et gérez l’identité git vous-même.
Laisser le runner configurer git
Démarrez le runner avec--configure-git, ou définissez SELF_HOSTED_RUNNER_CONFIGURE_GIT=1, pour qu’il écrive la configuration git globale au démarrage :
user.name = Claudeetuser.email = noreply@anthropic.com, correspondant aux sessions hébergées par Anthropic- Signature de commit et de tag au format SSH, routée via un shim géré par le runner qui signe chaque commit via le service de signature d’Anthropic en utilisant les identifiants de la session. Les signatures sont vérifiables sur GitHub par rapport à la clé de signature SSH publiée d’Anthropic.
push.negotiate = true, donc git demande à votre hôte git quels commits il possède déjà avant de préparer un push. Nécessite Claude Code v2.1.257 ou plus récent.core.hooksPathpointant vers un répertoire de hooks géré par le runner. Ses hookscommit-msgetprepare-commit-msgajoutent une remorqueCo-authored-by:pour le créateur de la session à chaque commit. La remorque est construite à partir de l’email dansCCR_SESSION_ACCOUNT_EMAILet omise quand cette variable n’est pas définie. Si votre image définit déjàcore.hooksPathet que le runner n’utilise pas git géré par Anthropic, le runner laisse votre paramètre en place, ignore l’installation de ces hooks, et affiche un avertissement[runner:git].
checkout ou post-session sont également signés au nom de la session, sans la remorque Co-authored-by:. Configuration git dans les hooks de cycle de vie décrit les paramètres git que le runner impose dans ces hooks.
Avec ou sans --configure-git, Claude Code demande à Claude de terminer ses messages de commit par une remorque Claude-Session: <url> et ses descriptions de pull request par l’URL de la session. Pour omettre les deux, définissez attribution.sessionUrl sur false dans le fichier ~/.claude/settings.json de l’hôte runner, puis redémarrez le runner.
Livrer la configuration git dans votre image
L’identité Git est requise pour tout commit. Définissez-la au niveau du système dans votre Dockerfile pour que la configuration s’applique indépendamment de l’utilisateur sous lequel le processus runner s’exécute :git commit échoue avec Please tell me who you are et les sessions ne peuvent pas progresser. Vous pouvez utiliser votre propre identité de bot à la place ; le runner ne remplace pas ces valeurs.
N’intégrez pas d’identifiants push longue durée ou à portée large dans une image runner partagée : un identifiant dans l’image est disponible pour chaque session que l’image exécute, peu importe qui l’a démarrée. À la place, créez un jeton de courte durée, à portée minimale, par session à partir de votre script wrapper, en utilisant l’identité du créateur de session décodée du JWT de session. Associez-le à un conteneur éphémère par session, qui nécessite --capacity 1, pour qu’aucun identifiant ne survive à la session qui l’a créé ; consultez la section de durcissement.
Si vous devez configurer les identifiants push au niveau de l’image, par exemple pour une clé de déploiement en lecture seule, limitez-les aussi étroitement que votre hôte git le permet :
- Une clé de déploiement SSH limitée à un référentiel avec une réécriture
url.<base>.insteadOf - Un
credential.helperqui retourne un jeton à portée minimale GIT_SSH_COMMANDpointant vers une clé à portée étroite
- Le runner définit
GIT_TERMINAL_PROMPT=0, donc git ne demande pas de nom d’utilisateur ou de mot de passe. - Le runner exécute SSH avec
BatchMode=yes, ajouté à votreGIT_SSH_COMMANDsi vous en définissez un, donc SSH ne demande pas de phrase de passe ou de confirmation d’hôte. - Le runner définit
GCM_INTERACTIVE=never, donc Git Credential Manager n’ouvre pas de dialogue de connexion. - Le runner efface
core.askPass, donc si vous utilisez un helper askpass, définissez-le via la variable d’environnementGIT_ASKPASSà la place.
GIT_SSH_COMMAND ou GIT_ASKPASS où les sessions ne peuvent pas y écrire, de la manière que la liste de contrôle de durcissement demande pour le répertoire de hooks et le script wrapper. Il en va de même pour toute clé ou fichier sur la ligne de commande de ce programme. Le git propre du runner exécute ce programme quand il clone ou récupère.
Si les répertoires de checkout sont possédés par un uid différent du processus runner, git refuse d’opérer sur eux ; ajoutez safe.directory :
Utiliser le proxy git Anthropic
Avec le proxy git Anthropic, également appelé git géré par Anthropic, l’image du runner n’a besoin d’aucune clé SSH, d’aucun credential helper, d’aucun.netrc ni d’aucun autre identifiant git pour la session elle-même. À la place, le runner demande à Anthropic de servir git pour ses sessions. Pour la session d’un utilisateur qu’Anthropic sert, le clone du runner ainsi que les récupérations et les pushs propres à la session passent par Anthropic, qui utilise le jeton OAuth GitHub stocké pour le créateur de la session. Comment Anthropic sert git pour une session couvre les sessions de bot et d’agent.
Le proxy git est désactivé tant que vous ne l’activez pas. Un runner qui atteint votre hôte git avec ses propres identifiants n’en a pas besoin, et son git fonctionne avec n’importe quel hôte git.
En contrepartie, le proxy git limite ce que le runner prend en charge et modifie ce dont il a besoin :
- github.com uniquement : Anthropic ne sert une session que lorsque tous ses dépôts sont sur github.com, et le proxy git ne prend pas encore en charge GitHub Enterprise Server. Sur un runner avec le proxy git, une session ayant un dépôt sur un autre hôte git ne démarre pas.
- Identifiants pour les dépôts de la session uniquement : Anthropic fournit des identifiants git pour les dépôts qui font partie de la session, et non pour les autres dépôts du même hôte git. Un sous-module privé, une dépendance que votre gestionnaire de paquets récupère avec git, ou une marketplace de plugins dans un autre dépôt n’obtient aucun identifiant d’Anthropic. Demandez aux personnes qui créent des sessions d’ajouter chaque dépôt dont une session a besoin lors de sa création. Chaque exécution d’une tâche planifiée cloud est une session avec les dépôts de la tâche ; demandez donc au propriétaire de chaque tâche d’ajouter chaque dépôt dont ses exécutions ont besoin.
- Pushs de branches uniquement : un push qui supprime une branche échoue, tout comme un push vers tout autre type de ref, comme un tag. Pour savoir quelles branches un push peut mettre à jour, consultez Proxy GitHub.
- Comptes GitHub connectés : demandez à chaque personne qui crée des sessions utilisateur avec un dépôt de connecter GitHub. Sans leur propre connexion, ces sessions ne démarrent pas.
-
--capacity 1: le proxy git nécessite une session par processus runner, donc exécutez plus de répliques pour le parallélisme. Activer le proxy git Anthropic liste les exigences. - Configuration git globale remplacée : le runner supprime et remplace la configuration git globale de l’utilisateur sous lequel il s’exécute. Exécutez-le en tant qu’utilisateur dédié ou dans un conteneur.
-
Identifiants de l’hôte pour les pushs de l’hôte : le push
--push-outcome-on-releasedu runner et tout push effectué par votre hookpost-sessionutilisent toujours les propres identifiants git de l’hôte runner et son chemin réseau versgithub.com. Gardez ces identifiants hors de la configuration git globale, et consultez Livrer la configuration git dans votre image pour les façons de les fournir. - Décision par session : Anthropic décide pour chaque session du runner s’il sert son git, et une session qu’il ne sert pas ne démarre pas. Quand des sessions ne démarrent pas sur un runner avec le proxy git couvre les causes.
safe.directory, dans la configuration git système.
Activer le proxy git Anthropic
Avant de démarrer le runner avec--use-anthropic-git-proxy, vérifiez que l’hôte runner satisfait chacune de ces exigences. Le runner refuse de démarrer lorsque l’exigence de capacité ou de git n’est pas satisfaite :
- Claude Code v2.1.267 ou plus récent : les versions antérieures acceptent le flag mais ne transmettent pas la demande pour qu’Anthropic serve git et n’affichent pas la ligne
Registering as opted in, donc Anthropic ne sert pas leurs sessions. --capacity 1, la valeur par défaut : chaque processus runner gère une session à la fois, donc exécutez plus de répliques pour le parallélisme.- Git 2.32 ou plus récent : les anciennes versions de git ignorent la configuration git par session que le runner met en place pour le proxy git.
--use-anthropic-git-proxy à la commande du runner, ou définissez CLAUDE_RUNNER_USE_GIT_PROXY=1 dans l’environnement du runner. Cette commande, exécutée dans un shell sur l’hôte runner, démarre le runner du démarrage rapide avec le proxy git activé :
Registering as opted in to Anthropic-managed git (--use-anthropic-git-proxy). Anthropic décide ensuite pour chaque session de ce runner s’il sert son git. Pour chaque session qu’il sert, le runner journalise une ligne [runner:session] contenant governed git ACTIVE. Si une session ne démarre pas, consultez Quand des sessions ne démarrent pas sur un runner avec le proxy git.
Comment Anthropic sert git pour une session
Pour une session qu’Anthropic sert, le clone du runner ainsi que les récupérations et les pushs propres à la session passent par Anthropic, authentifiés avec le jeton de courte durée propre à la session :- Sessions utilisateur : Anthropic utilise le jeton OAuth GitHub stocké pour le créateur de la session.
- Sessions de bot et d’agent : Anthropic utilise le jeton d’installation GitHub App de votre organisation.
- Réécritures d’URL :
--git-host-rewriteet--git-ssh-rewriten’ont aucun effet sur un dépôt que le proxy git sert.
Quand des sessions ne démarrent pas sur un runner avec le proxy git
Sur un runner démarré avec--use-anthropic-git-proxy, une session ne démarre pas lorsqu’Anthropic ne sert pas son git. Recherchez dans le log du runner une erreur git qui nomme une adresse api.anthropic.com contenant /git_proxy/.
Pour chaque session, un runner sur Claude Code v2.1.267 ou plus récent journalise également soit une ligne [runner:session] contenant governed git ACTIVE lorsqu’Anthropic sert le git de la session, soit une ligne [runner:warn] contenant the server withheld Anthropic-managed git for this session lorsqu’il ne le sert pas. Trouvez la ligne que vous voyez parmi ces cas :
- Ni
governed git ACTIVEni la lignewithheld: un runner antérieur à Claude Code v2.1.267 ne journalise aucune des deux lignes, et Anthropic ne sert pas ses sessions. Mettez à jour le runner vers v2.1.267 ou plus récent en suivant Épingler la version. - La ligne
withheld: Anthropic n’a pas servi la session. Un runner qui fonctionnait auparavant avec le proxy git peut échouer ainsi sans aucun changement de votre côté.- Un dépôt n’est pas sur github.com : une session ayant ne serait-ce qu’un dépôt sur un autre hôte git, comme GitHub Enterprise Server, n’est pas servie, y compris ses dépôts github.com. Désactivez le proxy git Anthropic pour les runners de cet environnement.
- Tous les dépôts sont sur github.com : signalez l’échec à votre équipe de compte Anthropic avec l’identifiant de session figurant dans la ligne
withheld. Anthropic enregistre la raison de son côté.
- Une ligne contenant
remote: access denied by the git proxy: une session qu’Anthropic sert peut tout de même être refusée, par exemple lorsque la politique de l’organisation refuse l’accès git pour la session, ou que la session n’est pas autorisée pour le dépôt. Le log du runner affiche alors une ligne contenantremote: access denied by the git proxy, et le reste de cette ligne en indique la raison. GitHub authentication required: ce message apparaît lorsque le créateur de la session n’a pas de connexion GitHub fonctionnelle sur claude.ai. Le clone de la session échoue, et l’erreur git indiqueGitHub authentication required. Please reconnect your GitHub account.Demandez à cette personne de connecter ou reconnecter GitHub dans ses paramètres claude.ai.
Désactiver le proxy git Anthropic
Si des sessions d’un environnement utilisent un dépôt sur un hôte git autre que github.com, comme GitHub Enterprise Server, désactivez--use-anthropic-git-proxy pour les runners de cet environnement.
1
Retirer le flag
Retirez
--use-anthropic-git-proxy de la commande du runner. Si vous avez défini CLAUDE_RUNNER_USE_GIT_PROXY dans l’environnement du runner, comme une spécification de pod ou un fichier Compose, retirez-la à cet endroit. Dans un shell, supprimez sa définition :2
Fournir des identifiants git au runner
Fournissez des identifiants qui fonctionnent sans invite pour chaque hôte git utilisé par les sessions des runners, github.com compris. Tout identifiant qui se trouvait dans la configuration git globale de l’utilisateur du runner a disparu, car le runner a supprimé cette configuration tant que
--use-anthropic-git-proxy était défini. Livrez les identifiants dans votre image ou utilisez un hook de cycle de vie checkout.3
Ouvrir le chemin réseau
Autorisez le runner à atteindre chaque hôte git utilisé par les sessions des runners sur le port 443 ou 22. Consultez la ligne relative à l’hôte git dans Exigences réseau.
4
Redémarrer les runners
Redémarrez les runners pour qu’ils s’enregistrent sans le proxy git. Puis redémarrez chaque session qui a échoué.
Accès à l’API GitHub sans la GitHub CLI
Si l’image de votre runner n’inclut pas la GitHub CLI, Claude Code peut fournir ungh intégré, afin que Claude puisse toujours ouvrir des pull requests, commenter et lire les résultats de CI. Le gh intégré est destiné aux runners qui utilisent git géré par Anthropic. Il prend en charge une seule commande, gh api, qui appelle l’API REST de GitHub. Nécessite Claude Code v2.1.287 ou plus récent dans l’image du runner.
Cette commande ouvre une pull request à la place de gh pr create. Le gh intégré renseigne {owner} et {repo} pour le dépôt actuel :
- Identifiants : le
ghintégré envoie ses requêtes REST via git géré par Anthropic, qui fournit l’identifiant GitHub du côté d’Anthropic, donc l’image n’a besoin d’aucun jeton GitHub pour celui-ci - Quelles sessions en bénéficient : Anthropic décide pour chaque session si git géré par Anthropic sert le
ghde la session. Lorsque c’est le cas, la ligne[runner:session] governed git ACTIVEque le runner journalise pour la session affichegh_path_shim=true. Sinon, la session n’a pas degh jq: installezjqdans l’image si vous voulez que--jqfonctionneCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: si l’environnement de la session la définit, Claude Code ne fournit pas leghintégré, et la session n’a pas degh
Faire confiance à une autorité de certification privée avec git géré par Anthropic
Cette section s’applique si vous définissezGIT_SSL_CAINFO ou GIT_SSL_NO_VERIFY dans l’environnement d’un runner dont les sessions utilisent git géré par Anthropic. La gestion qu’elle décrit nécessite que le runner exécute Claude Code v2.1.283 ou plus récent.
Quand git sur le runner doit faire confiance à une autorité de certification (CA) privée, comme celle qu’un proxy d’inspection TLS signe, les approches habituelles fonctionnent comme suit :
- Magasin de certificats système : installez votre CA dans le magasin de certificats système de l’hôte runner, et git lui fait confiance sans l’une ou l’autre variable.
GIT_SSL_CAINFO: définissez-la sur un fichier PEM de vos CAs, par exempleGIT_SSL_CAINFO=/etc/ssl/corp-ca.pem.GIT_SSL_NO_VERIFY: n’aide pas derrière un proxy de re-signature. Le clone propre du runner via git géré par Anthropic vérifie les certificats même quand la variable est définie, donc ce clone échoue jusqu’à ce que git fasse confiance à votre CA via l’une des deux autres approches.
command commence par l’environnement de la session, donc il obtient ce que git à l’intérieur de la session obtient :
GIT_SSL_CAINFO: ce par rapport à quoi git vérifie git géré par Anthropic dépend de l’endroit où git s’exécute :- Clone et récupérations propres du runner : s’exécutent sans la variable et vérifient git géré par Anthropic par rapport à un fichier de certificat par session que le runner écrit. Le runner construit ce fichier à partir du bundle CA système de l’hôte runner et des certificats de votre fichier.
- Git à l’intérieur de la session : obtient la configuration
http.sslCAInfonommant votre fichier à la place de la variable, plus les entréeshttp.<url>.sslCAInfoqui vérifient git géré par Anthropic par rapport au fichier par session. - Hooks
checkoutetpost-session: héritent la variable inchangée.
GIT_SSL_NO_VERIFY: quelles vérifications de certificat restent désactivées dépend de l’endroit où git s’exécute :- Clone et récupérations propres du runner : s’exécutent sans la variable et vérifient le certificat qui leur est présenté.
- Git à l’intérieur de la session : obtient la configuration
http.sslVerify=falseà la place de la variable, donc les vérifications restent désactivées pour les autres hôtes. Il obtient également des entréeshttp.<url>.sslVerify=truequi gardent les vérifications activées pour git géré par Anthropic. - Hooks
checkoutetpost-session: quand la session a un référentiel sur git géré par Anthropic, obtiennent la configurationhttp.sslVerify=falseà la place de la variable. Ils obtiennent également des entréeshttp.<url>.sslVerify=truequi gardent les vérifications activées pour git géré par Anthropic.
/etc/ssl/certs/ca-certificates.crt ou /etc/pki/tls/certs/ca-bundle.crt sur l’hôte runner. Il a également besoin d’un fichier GIT_SSL_CAINFO que l’utilisateur du runner peut lire, qui contient des blocs PEM CERTIFICATE, et qui fait au maximum 1 MiB. Quand le runner ne peut pas construire le fichier par session, il enregistre une ligne [runner:warn] contenant did not build the certificate file et la raison. Git utilise alors votre fichier tel quel pour git géré par Anthropic. Corrigez ce que la ligne nomme.
Pour chaque session qui utilise git géré par Anthropic, le runner enregistre également une ligne [runner:warn] qui commence par governed git: GIT_SSL_CAINFO is set ou governed git: GIT_SSL_NO_VERIFY is set. La ligne dit ce que le runner a fait avec cette variable pour son propre git, pour git à l’intérieur de la session, et pour vos hooks de cycle de vie. Elle se termine par si vous devez changer quelque chose.
Ajouter des dépôts github.com aux sessions de canal Claude Tag
Si Claude ne parvient pas à ajouter un dépôt github.com à une session de canal Claude Tag, assurez-vous que le canal y a accès, même lorsque votre runner peut le cloner. Cet accès provient de paramètres sur GitHub et dans votre organisation Claude, et non des identifiants git de votre runner.1
Installer la GitHub App Claude
Installez la GitHub App Claude sur l’organisation ou le compte GitHub propriétaire du dépôt, et incluez ce dépôt dans l’installation.
2
Lier l'organisation ou le compte GitHub
Dans Paramètres de l’organisation > Fournisseurs Git, liez cette organisation ou ce compte à votre organisation Claude. Un Owner de votre organisation Claude qui est également propriétaire de l’organisation GitHub peut la lier. Consultez Lier votre organisation GitHub.
3
Accorder l'accès au dépôt
Dans les paramètres d’administration de Claude Tag, accordez le dépôt au canal ou à une portée plus large qui inclut le canal : son espace de travail Slack ou l’ensemble de Slack. Une personne ayant le rôle Owner ou la permission Claude Tag Admin peut l’accorder. Un gestionnaire de canal peut également l’ajouter à son propre canal si son compte GitHub est administrateur du dépôt. Consultez Accorder l’accès au dépôt.
Réécrire les URL git pour les réseaux privés
Les URL de dépôt arrivent du plan de contrôle en HTTPS, avec le nom d’hôte de votre hôte git ; pour GitHub Enterprise, c’est le nom d’hôte que vous avez configuré pour l’intégration GitHub Enterprise sur claude.ai. Deux flags répétables réécrivent ces URL avant le clone :--git-host-rewrite <from>=<to>: pour le DNS à horizon divisé, où Anthropic atteint votre hôte git via un nom d’hôte externe mais les runners doivent utiliser un interne--git-ssh-rewrite <host>: pour les hôtes git qui n’acceptent que SSH, ou un hôte runner qui ne dispose que d’une clé SSH. Par exemple, pour cloner des dépôts github.com via SSH, ajoutez--git-ssh-rewrite github.comà la commande du runner. Le runner réécrit alorshttps://github.com/owner/repoengit@github.com:owner/repo. SSH doit se connecter sans invite.
--git-ssh-rewrite si vous avez besoin des deux. Pour un contrôle complet du checkout, utilisez un hook de cycle de vie checkout.
Construire l’image du runner
Anthropic ne publie pas d’image runner pré-construite. Construisez la vôtre autour du binaireclaude, en superposant la chaîne d’outils que vos référentiels ont besoin : runtimes de langage, compilateurs, gestionnaires de paquets, et sidecars MCP.
Les recettes ci-dessous utilisent --capacity 4, donc un conteneur sert jusqu’à quatre sessions concurrentes du même propriétaire verrouillé. Cela ne fournit pas l’isolation du conteneur par session dans la section de durcissement : avant de connecter un environnement aux systèmes de production, soit exécutez les recettes à --capacity 1 avec un conteneur par session, soit utilisez les runners à la demande, qui gardent également le secret de l’environnement hors des hôtes exécutant les sessions. Si vous ajoutez le proxy git Anthropic à l’une de ces recettes, changez également --capacity à 1.
Ce Dockerfile est un point de départ minimal. Il installe python3 car les hooks fournis par le plan de contrôle d’Anthropic aux sessions Claude Tag, y compris celui qui permet à Claude de publier sa réponse dans Slack, exécutent python3. Conservez python3 dans le PATH de toute image qui sert des sessions Claude Tag :
linux-x64 pour linux-arm64 si vos nœuds sont ARM, ou pour linux-x64-musl ou linux-arm64-musl sur une image basée sur musl comme Alpine ; consultez Configuration Alpine Linux pour les paquets supplémentaires dont les images musl ont besoin. L’URL est l’emplacement de version standard de Claude Code, donc vous pouvez vérifier le binaire téléchargé par rapport au manifeste signé de la version comme décrit dans Intégrité binaire et signature de code. Le runner nécessite Claude Code version 2.1.224 ou ultérieure, ou 2.1.229 ou ultérieure pour servir des sessions Claude Tag. Construisez l’image, puis poussez-la vers votre registre et référencez-la dans les recettes ci-dessous :
stable actuel et le transmet comme argument de construction, donc l’exécution de la même commande après une nouvelle version stable reconstruit la couche de téléchargement avec le binaire plus récent. Pour épingler une version spécifique pour des constructions reproductibles, transmettez le numéro de version directement comme CLAUDE_CODE_VERSION. Remplacez stable par latest dans l’URL de recherche lorsque vous avez besoin d’une version plus récente que le canal stable, comme celle qu’un modèle nouvellement lancé nécessite.
Dimensionner le CPU et la mémoire pour les sessions
Dimensionnez le conteneur ou l’hôte d’un runner pour les sessions qu’il exécute plutôt que pour le processus runner lui-même. Le runner lui-même interroge le système pour trouver du travail, prépare le checkout de chaque session, exécute vos hooks de cycle de vie, et démarre et supervise les processus de session. La charge provient des sessions : chacune est un processus Claude Code plus tout ce qu’il démarre, comme les builds, les suites de tests, les installations de paquets, et les serveurs MCP. Pour une session, commencez par les valeurs suivantes, exprimées comme des demandes et des limites Kubernetes ou l’équivalent de votre plateforme, et traitez-les comme un point de départ plutôt que comme une exigence :- Mémoire : une demande et une limite de 4 Gio chacune, ce qui satisfait le minimum de 4 Go dans les exigences système de Claude Code. Gardez les deux égales afin que le planificateur tienne compte de la mémoire complète du conteneur. Lorsque le conteneur atteint sa limite de mémoire, le noyau tue les processus à l’intérieur, ce qui peut terminer une session en cours de tâche.
- CPU : une demande de 2 CPUs et une limite de 4 CPUs, afin qu’une session puisse dépasser la demande lors des builds. Le noyau limite un conteneur à sa limite de CPU plutôt que de tuer les processus à l’intérieur, donc les sessions à la limite s’exécutent plus lentement mais continuent de s’exécuter.
resources suivant :
--capacity pour limiter le nombre de sessions qu’il exécute à la fois. Il ne divise pas le CPU ou la mémoire entre elles, donc les sessions sur un runner partagent le CPU et la mémoire du conteneur. Pour éviter que la consommation de mémoire d’une session ne mette fin aux autres, exécutez une session par runner avec sa propre limite de mémoire.
-
Une session par runner : donnez à chaque conteneur les valeurs d’une session. Utilisez ce dimensionnement à
--capacity 1, que la section de durcissement recommande, et pour les runners à la demande, où vous définissez les valeurs sur la charge de travail que votre hookspawn-runnersoumet, comme un modèle de pod de Job Kubernetes. -
Plusieurs sessions par runner : à un
--capacitysupérieur à un, multipliez les valeurs d’une session par la capacité, car jusqu’à ce nombre de sessions peuvent s’exécuter dans le conteneur en même temps. Les recettes Kubernetes et Docker Compose exécutent--capacity 4sans limites de CPU ou de mémoire, donc ajoutez des limites dimensionnées pour la capacité que vous exécutez. -
Plusieurs conteneurs de runner sur un même hôte : dimensionnez l’hôte pour la somme des capacités des runners, car chaque runner ne décompte que ses propres sessions par rapport à
--capacity:- Mémoire : multipliez la mémoire d’une session par cette somme, puis ajoutez ce qu’utilisent les propres processus de l’hôte
- CPU : multipliez la demande de CPU d’une session par cette somme. Pour permettre à toutes les sessions de dépasser leur demande en même temps, multipliez plutôt sa limite de CPU.
--base-dirdans chacun, sur un stockage qu’aucun autre conteneur de runner ne monte, afin que chaque runner dispose de son propre répertoire de travail.
Donner à chaque session sa propre limite de mémoire
Pour empêcher une session de mettre fin aux autres lorsque la mémoire vient à manquer, exécutez chaque runner à--capacity 1 sous sa propre limite de mémoire, définie à la mémoire d’une session :
- Dans un conteneur : exécutez un runner par conteneur, et définissez la limite de mémoire du conteneur, comme
limits.memorysur Kubernetes,--memorysur Docker, oumem_limitdans un fichier Compose. Gardez la somme des limites des conteneurs en dessous de la mémoire de l’hôte, car lorsque l’hôte lui-même manque de mémoire, le noyau peut tuer des processus n’importe où sur l’hôte. - Sous systemd sans conteneur : définissez
MemoryMaxsur l’unité du runner, et exécutez un runner par hôte, car les runners qui utilisent le même--base-dirsur un même système de fichiers partagent les dépôts qui y sont clonés. Par exemple, à--capacity 1sans hookcheckout, leurs sessions sur un même dépôt peuvent travailler simultanément dans le même checkout. Pour exécuter plusieurs runners sur un même hôte, donnez à chacun un conteneur.
--capacity au-dessus de un, les sessions partagent une seule limite. Lorsqu’elles l’atteignent, le noyau ne choisit pas par session les processus à tuer, donc la consommation de mémoire d’une session peut mettre fin aux autres.
Sur Kubernetes, vous ne pouvez séparer les sessions avec aucune des valeurs du paramètre singleProcessOOMKill du kubelet. Vous ne pouvez pas non plus plafonner la mémoire totale d’une session avec un ulimit de mémoire dans votre script wrapper. Le noyau applique une valeur comme ulimit -v à chaque processus séparément.
Laisser de la marge sous les limites de processus et de threads
Pour dimensionner une limite de processus et de threads, mesurez ce dont une session a besoin et multipliez-le par le nombre de sessions qui partagent la limite. Lorsque ces sessions atteignent la limite, le noyau refuse les nouveaux processus et threads, de sorte que les commandes, les builds et les tests de n’importe laquelle d’entre elles peuvent échouer. L’appel refusé renvoieEAGAIN, dont le message standard est Resource temporarily unavailable.
Supposons qu’une session atteigne un pic de 300 processus et threads, et que quatre sessions partagent la limite. Ensemble, elles ont besoin de 1 200, et avec une limite de 2 400, vous laissez de la marge pour des builds qui en nécessitent deux fois plus.
Où définir chaque limite
Chaque limite de ce tableau compte les threads ainsi que les processus, etpids.max est le fichier qui contient la limite d’un cgroup :
Lorsque vous ne définissez pas
TasksMax, systemd applique DefaultTasksMax, qui vaut par défaut 15 % de la plus petite des valeurs parmi kernel.pid_max, kernel.threads-max et le pids.max du cgroup racine.
Mesurer le pic d’une session
Pour obtenir les chiffres de vos propres runners, mesurez une session sur son hôte, qui, sur Kubernetes, est le nœud :1
Démarrer un runner seul
Démarrez un runner à
--capacity 1 sans rien d’autre dans son conteneur ou son unité systemd, seul sur son hôte et dans son environnement :- Son hôte : utilisez-en un qui n’exécute aucun autre runner. Vous pourrez alors repérer son processus à l’étape suivante.
- Son environnement : faites-en le seul runner présent, par exemple dans un environnement que vous créez pour le test. Les sessions sont acheminées vers un environnement et non vers un runner.
2
Trouver le répertoire cgroup du runner
Sur l’hôte, et non à l’intérieur du conteneur, trouvez l’ID de processus du runner, par exemple avec
ps. Lisez /proc/<pid>/cgroup pour cet ID :- Une ligne contient
:pids:: l’hôte utilise cgroup v1, et le répertoire est/sys/fs/cgroup/pidssuivi du texte qui suit:pids: - La seule ligne commence par
0::: l’hôte utilise cgroup v2, et le répertoire est/sys/fs/cgroupsuivi du texte qui suit0::
3
Exécuter un build représentatif et relever le pic
Faites passer une session par un build représentatif, et relevez le pic depuis le répertoire cgroup du runner :
- Le répertoire contient
pids.peak: lisez le fichier une fois le build terminé - Le répertoire ne contient pas
pids.peak: lisezpids.currentune fois par seconde pendant l’exécution du build, et conservez la valeur la plus élevée. Un pic survenant entre deux lectures passe inaperçu, considérez donc cette valeur comme une estimation basse.
4
Multiplier par les sessions qui partagent la limite
- Le Pod, le conteneur ou l’unité d’un seul runner : multipliez le pic par le
--capacitydu runner - Une limite que plusieurs runners partagent : multipliez le pic par la somme de leurs capacités
5
Définir la limite au-dessus du résultat
Avec une limite plus élevée, vous laissez de la marge pour des builds plus lourds, et avec une limite plus basse, vous arrêtez plus tôt une fork bomb.
Vérifier une limite pids.max
- Vérifier si
pids.maxa été atteinte : recherchez dans le log du noyau de l’hôte, par exemple avecdmesg, une ligne contenantcgroup: fork rejected by pids controller inet le chemin d’un cgroup. Le noyau ne la consigne que la première fois qu’il refuse un processus ou un thread dans ce cgroup, recherchez donc dans l’ensemble du log. - Voir la limite en vigueur : lisez
pids.maxdans le répertoire cgroup du runner et dans chaque répertoire situé au-dessus. La valeur la plus basse est la limite en vigueur, etmaxsignifie qu’il n’y a aucune limite à ce niveau.
Kubernetes
Le runner sertGET /healthz sur le port 8080 par défaut, configurable avec --health-port, donc les sondes Kubernetes fonctionnent sans configuration supplémentaire. Le point de terminaison retourne 200 chaque fois que le processus est vivant, donc les sondes ci-dessous détectent un processus mort, pas un bloqué ; pour attraper un runner qui a arrêté d’interroger, alertez sur la série last_poll_age_seconds de /metrics. Le Deployment ci-dessous monte le secret de l’environnement à partir d’un Secret Kubernetes, pointe les sondes de vivacité et de disponibilité vers /healthz, et définit une période de grâce de terminaison de 90 secondes. Consultez Timing d’arrêt pour savoir pourquoi la période de grâce est importante.
Le manifeste ne définit pas de resources CPU ou mémoire sur le conteneur runner. Ajoutez un bloc dimensionné pour la capacité que vous exécutez, comme Dimensionner le CPU et la mémoire pour les sessions le décrit.
claude-runners. Créez d’abord l’espace de noms :
(umask 077 && cat > ./environment-secret), collez le secret, appuyez sur Entrée, puis Ctrl-D. Ensuite, créez le Secret et supprimez le fichier :
Docker Compose
Le service Compose ci-dessous redémarre le runner chaque fois qu’il se termine, ce qui couvre à la fois les crashes et la sortie normale après le drainage. Une politique de redémarrage Docker redémarre le même conteneur avec sa couche inscriptible intacte, donc le runner revient sur un système de fichiers réutilisé plutôt que le frais que la posture de durcissement recommande ; utilisez cette recette pour l’évaluation, et pour la production soit recréez le conteneur par exécution, soit utilisez un orchestrateur qui le fait. Docker attend plus longtemps avant chaque redémarrage d’un conteneur qui continue de se terminer, jusqu’à un plafond, donc un runner qui ne peut pas démarrer ne continue pas à redémarrer dans une boucle serrée selon cette recette. Quand le runner se termine décrit ce qu’il faut vérifier quand cela se produit.Délai d’arrêt
AprèsSIGTERM, un runner a besoin de temps pour arrêter proprement ses sessions avant que votre orchestrateur ne le tue. Il journalise cette durée au démarrage, dans une ligne qui contient This runner needs up to 80s avec les paramètres par défaut. Définissez le délai d’arrêt de votre orchestrateur à au moins ce nombre de secondes : terminationGracePeriodSeconds sur Kubernetes, stop_grace_period sur Docker Compose, ou l’équivalent de votre plateforme. Kubernetes utilise 30 secondes par défaut, donc sans ce paramètre il peut arrêter le pod avant que le runner ne termine.
À la réception de SIGTERM, le runner cesse d’accepter de nouvelles sessions. Il commence ensuite un arrêt progressif, appelé drainage, sauf si vous définissez --defer-shutdown-max-min pour le retarder. Le drainage comporte trois étapes :
- Le runner attend jusqu’à
--drain-wait-secsecondes,0par défaut, que les tours encore en cours se terminent. - Il termine l’arborescence des processus de chaque session, y compris toute commande que Claude exécutait encore, mais pas un processus toujours en cours d’exécution après la fin de sa commande shell.
- Il exécute le hook de cycle de vie
post-session.
post-session sauvegarde encore le travail non validé.
La durée journalisée est la somme de ces valeurs :
--drain-wait-secpour l’étape 1, 0 seconde par défaut--session-stop-grace-secpour l’étape 2, 5 secondes par défaut--post-session-hook-timeout-secpour l’étape 3, 60 secondes par défaut- Une marge fixe de 15 secondes
- 30 secondes supplémentaires lorsque
--push-outcome-on-releaseest défini
--capacity plus élevée ne l’augmente pas, car le runner draine toutes ses sessions en même temps.
Prévoyez plus de temps si vous définissez l’un de ces flags :
- Avec
--retire-at: laissez suffisamment de temps entre l’heure de retraite et l’heure d’arrêt de l’hôte pour que les tours typiques se terminent, plus l’attente des tâches en arrière-plan que Cycle de vie du runner décrit, plus la durée journalisée. Calculez l’heure de retraite à chaque lancement, par exempledate +%splus la durée de vie prévue du runner. - Avec
--defer-shutdown-max-min: le délai d’arrêt doit également couvrir les minutes que vous configurez et une attente supplémentaire avant le début du drainage, 75 secondes avec les paramètres par défaut. Différer le drainage au-delà du premier signal explique les deux. Le runner journalise également cette durée plus longue au démarrage.
Laisser les tours en cours se terminer avant un redémarrage
Pour laisser les tours en cours se terminer avant un redémarrage progressif, définissez--drain-wait-sec et augmentez votre délai d’arrêt à au moins cette attente plus 80 secondes. Avec l’attente par défaut de 0, un redémarrage interrompt tout tour encore en cours, et la session reprend sur un autre runner sans son travail non poussé.
--drain-wait-sec: à peu près la durée des tours et des tâches en arrière-plan que vous voulez laisser se terminer. Il s’agit d’un maximum : l’attente prend fin plus tôt s’ils se terminent avant.- Votre délai d’arrêt :
terminationGracePeriodSecondsdans Kubernetes,stop_grace_perioddans Docker Compose. Par exemple, avec--drain-wait-sec 120, remplacez le90dans la recette Kubernetes ou Docker Compose par200. - Si vous avez modifié d’autres paramètres d’arrêt : les 80 secondes correspondent aux paramètres par défaut. Démarrez un runner avec vos flags, et utilisez au moins le nombre figurant dans sa ligne de démarrage
This runner needs up to, qui inclut l’attente. Avec--defer-shutdown-max-mindéfini, utilisez le nombre plus élevé de la ligne de démarrage suivante, aprèsSize the stop timeout to at least. Délai d’arrêt liste ce qui s’ajoute à ces nombres.
Publier un message d’état avant un redémarrage
Avant un redémarrage qui interrompra des sessions, demandez à un Owner de publier un message d’état sur l’environnement depuis la page d’administration Cloud environments. Sur claude.ai/code, les membres voient le message dans leurs sessions de cet environnement. Demandez à l’Owner de définir une date d’expiration lors de la publication, ou de supprimer le message une fois les runners de retour.Différer le drainage au-delà du premier signal
Définissez--defer-shutdown-max-min <n> si vous souhaitez qu’un runner que vous redémarrez continue de servir les sessions qu’il détient pendant jusqu’à n minutes, au lieu de les drainer au premier signal. À la première SIGTERM ou SIGINT, le runner cesse de prendre de nouveaux travaux et continue de servir les sessions qu’il détient. Il continue de sonder pour que le plan de contrôle ne remette pas ces sessions en file d’attente. Nécessite Claude Code v2.1.238 ou ultérieur.
Ce qui arrive aux sessions que le runner détient après le premier signal
Dans les deux premiers stades qui suivent le signal, le runner libère les sessions, et une session libérée reprend sur un runner frais lorsque son utilisateur envoie son prochain message. En comptant à partir du premier signal, le runner passe par trois stades :- Pendant les premiers
nminutes : le runner sert ses sessions normalement et continue d’appliquer--startup-timeout-minet--kill-session-after-min. Si vous définissez également--release-idle-session-min, le runner libère toute session dont l’utilisateur a été inactif aussi longtemps ; sans cela, les sessions inactives restent sur le runner. - Lorsque les
nminutes s’écoulent : le runner libère chaque session qu’il détient toujours, inactive ou non. Le runner attend la fin du tour d’une session en cours de tour, et jusqu’à 60 secondes supplémentaires pour les tâches de fond d’un tour, avant de libérer cette session. - Lorsque la grâce post-libération s’écoule : le runner draine les sessions qu’il détient toujours, et le plan de contrôle remet chaque session drainée en file d’attente vers un autre runner immédiatement. La grâce post-libération commence lorsque les
nminutes s’écoulent et est de 75 secondes par défaut. Si vous définissez--drain-wait-secau-dessus de 60 secondes, la grâce post-libération est--drain-wait-secplus 15 secondes à la place.
--defer-shutdown-max-min. Une fois qu’un drainage est en cours, le signal suivant force la sortie du runner. Cela s’applique qu’un deuxième signal ou l’expiration de la grâce post-libération ait commencé le drainage.
Dimensionner le délai d’arrêt
Donnez à votre délai d’arrêt de l’hôte au moins la somme de trois parties : lesn minutes que vous configurez, la grâce post-libération, et le drainage que Délai d’arrêt décrit. Avec les paramètres par défaut, la grâce post-libération est de 75 secondes et le drainage prend jusqu’à 80 secondes, donc autorisez n minutes plus 155 secondes. Le runner imprime cette somme au démarrage chaque fois que --defer-shutdown-max-min est défini.
Si le délai d’arrêt s’écoule avant que le runner ne termine, les sessions qu’il détient toujours ne reçoivent aucun hook post-session. Le runner ne se désenregistre pas, donc le plan de contrôle peut mettre quelques minutes, et parfois plus, à remettre ces sessions en file d’attente. Si vous ne pouvez pas donner au délai d’arrêt cette somme, laissez --defer-shutdown-max-min non défini pour que le runner draine au premier signal à la place.
Ce qui atteint un hook post-session en cours d’exécution
Le hookpost-session et l’enfant de session Claude s’exécutent chacun dans leur propre groupe de processus POSIX, séparé de celui du runner, donc les mécanismes d’arrêt les atteignent différemment :
- Un
SIGTERMtandis que le runner draine déjà : force la sortie du runner immédiatement, en sautant tout ce qui reste du drainage. Sans--defer-shutdown-max-min, c’est le deuxièmeSIGTERMque le runner reçoit. Rien ne signale un hookpost-sessionen cours d’exécution, donc sur un hôte nu où un processus init adopte les orphelins, il se termine de lui-même, mais sans supervision : son budget de délai d’expiration ne s’applique plus, et une écriture dans le pipe de log fermé peut le tuer avecSIGPIPE, donc un hook qui doit survivre à une sortie forcée là devrait rediriger sa propre sortie vers un fichier. Dans les recettes de conteneur sur cette page, le runner est le PID 1 du conteneur et sa sortie termine le conteneur, et sous leKillMode=control-grouppar défaut de systemd, la suppression au niveau du cgroup atteint également le hook, comme l’entrée Suppressions au niveau du cgroup le décrit ; dans les deux cas, traitez une sortie forcée comme fatale au hook et fiez-vous à la période de grâce à la place. - Signaux au niveau du groupe de processus, tels que
kill -- -<pid>dans un script wrapper, le contrôle des tâches du shell, ou un watchdog au niveau du groupe : atteignent le runner et un sous-processus de hookcheckouten cours, qui reste intentionnellement attaché au groupe, mais pas un hookpost-sessionen cours d’exécution ou l’enfant de session. - Suppressions au niveau du cgroup, telles que le
KillMode=control-grouppar défaut de systemd ou leSIGKILLque Kubernetes livre à tout le conteneur lorsqueterminationGracePeriodSecondsexpire : atteignent tout, y compris le hook. L’isolation du groupe de processus ne protège pas contre celles-ci, c’est pourquoi la période de grâce doit couvrir l’intégralité du drainage. - Le délai d’expiration du hook lui-même : lorsqu’un hook dépasse
--post-session-hook-timeout-sec, le runner envoieSIGTERMà tout le groupe de processus du hook, puisSIGKILLdeux secondes plus tard, donc un worker que le hook a forké, tel que tar, rsync, ou git, se termine avec le shell wrapper au lieu de survivre en tant qu’orphelin. La supervision du runner se termine une fois que le stdio du hook se ferme : un worker qui a redirigé sa propre sortie vers un fichier et survit à l’étapeSIGTERMest au-delà de la portée du runner.
post-session s’exécutent toujours, afin que vous puissiez distinguer un drainage silencieux d’un qui est en cours de snapshot.
Garder le répertoire de base et la capacité identiques sur tous les runners
Si un runner meurt en milieu de session, le serveur remet la session en file d’attente et un autre runner dans l’environnement la récupère. Ce runner dérive le chemin de checkout de ses propres--base-dir et --capacity : --capacity 1 se vérifie directement sous --base-dir, et un --capacity au-dessus de 1 utilise des worktrees par session à la place. Quand les runners dans le même environnement utilisent des valeurs différentes pour l’un ou l’autre drapeau, le répertoire de travail de la session reprise change, et les chemins absolus que l’agent a enregistrés plus tôt, dans les éditions, les appels d’outils, ou ses propres notes, pointent vers un emplacement qui n’existe plus.
Utilisez le même --base-dir et --capacity sur chaque runner dans un environnement, et n’utilisez pas une valeur par hôte comme un ID d’instance ou un nom d’hôte. Passez les deux flags explicitement, comme le font les recettes Kubernetes et Docker Compose, afin qu’aucune des deux valeurs ne dépende d’une valeur par défaut.
Le répertoire de base par défaut est /workspace, avec l’exception que la ligne de référence --base-dir enregistre. Le runner a besoin d’accès en écriture à celui-ci. Au démarrage, avant de s’enregistrer, le runner crée le répertoire et confirme qu’il peut y écrire, et quitte avec cannot create or write to base directory quand il ne peut pas. Un runner démarré en tant que root crée le /workspace par défaut lui-même. Pour un runner non-root, créez le répertoire et donnez la propriété à l’utilisateur du runner avant de démarrer le runner, ou pointez --base-dir vers un répertoire que cet utilisateur possède déjà.
Réutiliser un checkout pré-chauffé
Pour les grands dépôts, le clone peut dominer le démarrage de la session. Pour sauter le clone froid, fournissez vous-même un clone au chemin où le runner conserve le sien. Sans hookcheckout, le runner garde un clone canonique par dépôt à <base-dir>/<repo-owner>/<repo> et le réutilise entre les sessions :
- À
--capacity 1: le runner récupère la ref demandée, détacheHEAD, et réinitialise dur à celle-ci, ce qui est quasi-instantané quand peu a changé. - À une
--capacitysupérieure à un : le runner récupère dans ce clone, puis extrait un worktree distinct à partir de celui-ci pour chaque session. Un clone pré-chauffé économise le téléchargement, mais pas le checkout.
- Clone dans l’image : construisez le clone dans votre image runner à ce chemin. Chaque conteneur frais démarre alors avec le clone chaud sans réutiliser un disque.
- Clone sur un volume persistant : sur les runners que vous pré-verrouillez au compte d’un utilisateur avec
--lock-to-account, pointez--base-dirvers un volume persistant, pour que le disque ne serve que ce compte. Un runner pré-verrouillé ne récupère jamais les sessions de canal Claude Tag, donc cette option ne s’applique pas aux runners qui les servent.
-
Profondeur du clone : créez le clone sans
--depth, avec ou sans--single-branch. Le runner ne passe jamais--depthlorsqu’il récupère dans un clone doté d’un remoteorigin, même si vous définissezCLAUDE_RUNNER_FETCH_DEPTH. Avec un clone complet, chaque session ne télécharge que les commits qui manquent au clone. Avec un clone peu profond, une session sur une branche qui a divergé avant le commit le plus ancien du clone télécharge tout l’historique de cette branche. -
Les changements suivis se réinitialisent, les fichiers non suivis persistent : à
--capacity 1, chaque session commence à partir d’une réinitialisation dur qui efface les modifications suivies de la session précédente, mais le runner ne lance jamaisgit clean, donc les fichiers non suivis des sessions antérieures du propriétaire verrouillé restent dans l’arbre. -
Les répertoires par session persistent aussi : à côté du checkout, le runner crée des entrées par session sous
<base-dir>/_sessions/pour chaque session qu’il exécute. Le répertoire de configuration Claude de la session contient une copie locale de la transcription de la conversation. À côté se trouvent les fichiers téléchargés de la session, quand la session en a. Le répertoire de session s’y trouve aussi : il contient tous les worktrees par session et les checkouts du hookcheckoutpendant que la session s’exécute, et il conserve tout ce que Claude a écrit dedans. Par défaut, le runner les laisse en place quand la session se termine, donc sur un disque qui survit au processus runner, ils s’accumulent. Chaque session s’exécute en tant qu’utilisateur du runner, donc toute session ultérieure que ce disque sert peut les lire. Si vous conservez un--base-dirpersistant, dimensionnez le volume pour cette croissance. La même chose s’applique à toute configuration qui redémarre le runner sur le même système de fichiers, y compris la recette Docker Compose. -
Avec
--remove-session-state, les répertoires par session ne persistent pas : démarrez le runner avec--remove-session-statepour qu’il supprime les répertoires par session de chaque session à la fin de la session. La suppression est au mieux : les répertoires restent quand le runner est tué avant l’exécution de son nettoyage. Le clone canonique et les fichiers qu’une session a écrits ailleurs sur l’hôte, comme le répertoire temporaire, restent indépendamment. -
Avec le proxy git, la réinitialisation devient un checkout : avec
--use-anthropic-git-proxy, le runner assainit le.git/du clone avant chaque session. Il conserve le magasin d’objets et l’état peu profond, et supprime l’index. Sans l’index, chaque session commence par un checkout complet de la copie de travail au lieu d’une réinitialisation quasi-instantanée. Le runner récupère toujours dans le clone existant plutôt que de cloner à nouveau le dépôt. Les pré-chauffages de sous-module ne sont pas supportés sous le proxy. -
Avec le proxy git, un pré-chauffage
reftableperd ses refs : quand le runner assainit.git/avant chaque session, il conserve.git/refs/et supprime le répertoire.git/reftable/. Un clone pré-chauffé au format de refreftablede git perd ainsi ses refs ; pré-chauffez donc plutôt le clone au format de reffilesde git. - Les longs clones n’ont besoin d’aucune solution de contournement : le runner limite chaque opération git avec un watchdog sans progrès de 120 secondes et un plafond dur de 30 minutes, pas un délai d’expiration plat, donc un clone froid lent qui continue de signaler le progrès se termine.
Confirmer que le runner utilise votre clone
Pour confirmer que le runner utilise votre clone, démarrez-le avec--log-level debug et lancez une session sur le dépôt. Le runner consigne Prefetched repo found at et le chemin du clone. S’il consigne plutôt No prefetched repo, fresh init at, il n’a trouvé aucun clone utilisable au chemin indiqué dans cette ligne. Vérifiez que votre clone se trouve à ce chemin, qu’il appartient à l’utilisateur du runner et qu’il possède un remote nommé origin.
Si le runner trouve votre clone et que les sessions démarrent toujours lentement, vérifiez si le clone est peu profond.
Épingler la version
Par défaut, le processus enfant de chaque session exécute le binaire du runner lui-même, et le runner désactive la mise à jour automatique à l’intérieur des sessions qu’il génère. Avec ce comportement par défaut, chaque session exécute la version que vous avez installée sur l’hôte ou intégrée à l’image. Une mise à jour au niveau de l’hôte prend effet la prochaine fois que le runner démarre. Choisissez la version qu’exécutent vos sessions et le moment où elle change :- Avant d’épingler une version : vérifiez les versions de Claude Code que les modèles nécessitent pour chaque modèle que vos sessions utilisent. Si un modèle nécessite une version plus récente que celle qu’exécutent vos sessions, le serveur rejette les requêtes pour ce modèle avec Claude Code ne supporte pas ce modèle.
- Pour garder une flotte sur une version : construisez l’image avec une version épinglée, ou sur un hôte nu installez une version spécifique et désactivez les mises à jour automatiques
- Pour mettre à niveau une flotte fixe : lisez les entrées du changelog entre votre version et celle que vous installez, puis installez la version plus récente ou reconstruisez l’image et redémarrez les runners
- Pour mettre à niveau des runners à la demande : lisez les entrées du changelog entre votre version et celle que vous installez, puis modifiez l’image que démarre votre hook
spawn-runner. Chaque nouveau runner obtient la nouvelle version. Un runner déjà actif, y compris un runner en attente démarré par--min-idle, conserve sa version jusqu’à ce qu’il se termine. Ne le redémarrez pas, car son ordre de travail est à usage unique. - Si les sessions démarrent un binaire différent : si vous utilisez
--exec-pathou un hookcommandpour démarrer un binaire Claude Code autre que celui du runner, les sessions exécutent la version de ce binaire. Mettez à jour ce binaire lorsque vous mettez à jour le runner. - Plugins : les places de marché de plugins ne se mettent pas à jour automatiquement non plus ; définissez
FORCE_AUTOUPDATE_PLUGINS=1dans l’environnement du runner pour laisser les plugins se mettre à jour automatiquement pendant que le binaire reste épinglé
Mettre à l’échelle la flotte
Votre orchestrateur décide quand ajouter ou supprimer des runners. En raison du verrou d’un propriétaire par runner, le nombre minimum de répliques est le nombre d’utilisateurs et d’agents Claude Tag que vous vous attendez à être actifs simultanément ;--capacity contrôle le parallélisme au sein des sessions d’un propriétaire, pas entre les propriétaires.
Deux approches de mise à l’échelle sont disponibles :
- Flotte fixe : exécutez un ensemble statique de répliques runner et mettez à l’échelle sur les métriques Prometheus que chaque runner sert
- Runners à la demande : exécutez la sous-commande
claude self-hosted-runner orchestrator, qui interroge Anthropic pour les sessions en attente sans runner disponible et invoque votre hookspawn-runnerpour en démarrer un par session. Consultez Runners à la demande.
Libérer les sessions inactives sur une flotte fixe
Si des sessions restent en attente sur une flotte fixe alors que chaque runner est en ligne, les sessions inactives peuvent en être la cause. Par défaut, une session conserve son emplacement une fois son tour terminé, et son runner reste verrouillé sur le propriétaire de cette session. Si la tâche planifiée cloud de votre équipe démarre une nouvelle session à chaque exécution, ses sessions inactives peuvent occuper tous les emplacements. Pour vérifier, lisezclaude_code_self_hosted_runner_session_idle_seconds dans les métriques Prometheus de chaque runner. Chaque session détenue par un runner possède sa propre série : 0 tant que la session est occupée, sinon le nombre de secondes écoulées depuis qu’elle est devenue inactive. Choisissez la durée d’inactivité que vous autorisez : plus longue que les pauses que les gens font en cours de tâche, et suffisamment courte pour que chaque exécution soit libérée avant le démarrage de la suivante de la tâche planifiée. Comparez ensuite les séries à cette durée.
- Séries au-dessus de cette durée, comme
1800pour 30 minutes : des sessions inactives occupent ces emplacements, définissez donc--release-idle-session-min - Toutes les séries en dessous : aucune session n’est restée inactive aussi longtemps. Ajoutez des runners, ou définissez
--kill-session-after-minpour les sessions qui ne deviennent jamais inactives. - Un runner sans aucune série : aucune session ne l’occupe. Consultez Sessions stay queued sous Dépannage.
- Un runner dont les métriques ne répondent pas : son processus s’est peut-être arrêté. Consultez An environment that looks healthy sous Quand le runner s’arrête.
--release-idle-session-min suivi de cette durée en minutes, par exemple --release-idle-session-min 30. Le runner libère alors toute session qui reste inactive aussi longtemps, qu’elle ait été démarrée par une tâche planifiée ou par une personne.
- Travail non poussé : une session libérée reprend à son prochain message, sans le travail qu’elle n’avait pas poussé. Pour conserver ses commits, consultez Les sessions reprises perdent le travail non poussé.
- Arrêt du runner : un runner qui libère sa dernière session entame un arrêt normal. Faites-le redémarrer par votre superviseur sur un système de fichiers vierge, comme le décrit Renforcer votre déploiement.
- Sessions qui ne deviennent jamais inactives : définissez également
--kill-session-after-min, car certaines sessions ne comptent pas comme inactives
Problèmes connus et limitations
Voici les limitations de cette version, avec des solutions de contournement où l’une existe.Le trafic du connecteur quitte votre réseau
Anthropic appelle les outils connecteur à partir de sa propre infrastructure plutôt que de votre runner. Les outils connecteur sont les connecteurs claude.ai, tels que GitHub, Slack et Linear. Quand Claude utilise un connecteur dans une session auto-hébergée, ce trafic passe parapi.anthropic.com plutôt que d’originer à l’intérieur de votre limite réseau.
Pour garder un connecteur hors des sessions auto-hébergées, filtrez-le avec les paramètres de politique allowedMcpServers et deniedMcpServers. Claude Code applique ces paramètres aux connecteurs qu’Anthropic livre ainsi qu’aux serveurs que vous configurez à partir de l’hôte runner et aux serveurs que les utilisateurs ajoutent, donc si vous déployez une liste d’autorisation pour d’autres serveurs, Claude Code bloque également les connecteurs livrés. Pour garder les connecteurs disponibles aux côtés d’une liste d’autorisation basée sur URL, ajoutez des entrées qui correspondent aux chemins de proxy Anthropic pour les connecteurs livrés :
https://api.anthropic.com/v2/ccr-sessions/*https://api.anthropic.com/v1/code/sessions/*https://api.anthropic.com/v1/code/mcp/*
Certaines sessions ne comptent pas comme inactives
Une session tenant une tâche de fond qui ne se termine jamais ne compte pas comme inactive, donc--release-idle-session-min ne libérera pas l’emplacement de cette session. Une session qui attend une approbation demandée de l’intérieur d’un appel d’outil en cours d’exécution ne compte pas non plus comme inactive. Définissez toujours --kill-session-after-min à côté comme un arrêt dur pour qu’aucune session ne puisse tenir un emplacement indéfiniment.
--kill-session-after-min est un arrêt dur pour les sessions qui s’échappent. Sur un runner en v2.1.260 ou ultérieur, une session qui atteint la limite n’est pas terminée immédiatement. Le runner lui donne une fenêtre de grâce, 15 minutes par défaut, que vous pouvez modifier avec SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS :
- Si la session attend son utilisateur, le runner la libère. Si son tour s’est terminé et elle ne contient que des tâches de fond, le runner attend jusqu’à 60 secondes pour que ces tâches se terminent, puis la libère. La session reprend quand son utilisateur envoie son prochain message.
- Si un tour est toujours en cours d’exécution, le runner attend que le tour se termine, ou que la session attende ensuite son utilisateur, puis la libère.
- Si la session est toujours sur le runner quand la fenêtre de grâce se termine, le runner la termine, et tout travail du tour en cours d’exécution est perdu. Un tour attendant une approbation demandée de l’intérieur d’un appel d’outil en cours d’exécution est une façon pour une session de dépasser la fenêtre.
--kill-session-after-min 480 pour 8 heures. Pour libérer les emplacements des conversations qui deviennent inactives, utilisez --release-idle-session-min à la place.
Limitations supplémentaires
- Les sessions reprises perdent le travail non poussé : un nouveau runner clone à nouveau le dépôt à partir de sa branche de démarrage, donc le travail que la session n’avait pas poussé est perdu.
- Pour conserver le travail commité : définissez
--push-outcome-on-releasesur chaque runner de l’environnement, car un runner sans ce flag reprend la session à partir de sa branche de démarrage. Un runner avec ce flag effectue, dans la mesure du possible, un push des branches de résultat de la session avant de la libérer, et la session reprise démarre à partir de ces commits. Le push utilise les propres identifiants git de l’hôte runner, y compris sur un runner qui utilise git géré par Anthropic. - Avec un hook
checkout: les dépôts extraits via un hook de cycle de viecheckoutne font pas l’objet d’un push. Créez plutôt un instantané de ces dépôts à partir du hookpost-session. - Avant d’activer le flag : limitez qui peut pousser vers les refs
claude/*sur le dépôt distant source. À la reprise, le runner récupère la branche précédemment poussée sans vérifier qui l’a poussée. - Les modifications non commitées sont toujours perdues : pour demander à Claude de créer un commit et de pousser lorsqu’il a fini de répondre, consultez Demander aux sessions de pousser leur travail
- Pour conserver le travail commité : définissez
- Une commande interrompue par la perte d’un runner peut s’exécuter à nouveau : lorsque la session reprend sur un autre runner, Claude voit l’appel d’outil interrompu avec un résultat indiquant que son issue est inconnue.
- Le clonage d’un dépôt ajouté en milieu de session peut échouer : Claude le clone avec
git clonevia HTTPS. Sur un runner sans--use-anthropic-git-proxy, le clonage échoue avec une erreur d’authentification git si rien sur l’hôte ne peut lire le dépôt. Dans la mesure du possible, sélectionnez chaque dépôt dont la session a besoin quand vous la créez. - Certains connecteurs n’apparaissent pas dans les sessions auto-hébergées : un connecteur que vous n’avez pas encore connecté dans les paramètres claude.ai n’est pas listé dans une session auto-hébergée, et la session ne vous invitera pas à le connecter. Connectez-le d’abord dans les paramètres, puis démarrez une session fraîche. Ajouter un connecteur à une session déjà en cours d’exécution ne rend pas non plus ses outils disponibles à Claude ; démarrez une session fraîche pour récupérer un connecteur nouvellement ajouté.
- Quand quelqu’un archive ou supprime une session, ou libère son runner depuis le menu de la session : ne comptez pas sur le push de
--push-outcome-on-release. Pour sauvegarder les commits dans ces cas, poussezHEADvers une branche de secours à partir d’un hookpost-session. Si vous partez de l’exemple qui s’y trouve, faites-le pousser même lorsqu’il n’y a aucune modification non commitée, car il ne pousse qu’après un commit d’instantané.
Signaler un problème
Pour les problèmes avec les environnements auto-hébergés, contactez votre équipe de compte Anthropic.Dépannage
Pour un diagnostic guidé, exécutez la sous-commande doctor sur l’hôte du runner. La sous-commande doctor démarre une session Claude Code interactive à laquelle sont joints les logs et l’état du runner. Connectez-vous d’abord avecclaude auth login sur cet hôte afin que la session puisse interroger votre environnement, ses runners et ses sessions en file d’attente. Sans cette connexion, par exemple lorsque l’hôte s’authentifie avec une clé API, elle est limitée à l’endpoint de santé local, aux métriques et au log du runner, et elle ne lit ce log que si vous avez démarré le runner avec --log-file.
-
Le runner n’apparaît pas dans l’environnement : vérifiez que l’hôte peut joindre
api.anthropic.comen HTTPS, que le secret de l’environnement est à jour et que l’horloge de l’hôte est à moins de cinq minutes de l’heure réelle ; un décalage plus important fait échouer l’authentification. En cas d’échec d’authentification, le runner journalise[runner:fatal]avec le motif du rejet. -
Le runner s’arrête au démarrage avec
cannot create or write to base directory: le runner ne peut pas créer--base-dirni y écrire, ce répertoire étant/workspacepar défaut. Corrigez le propriétaire du répertoire ou faites pointer--base-dirvers un chemin accessible en écriture, comme décrit dans Conserver un répertoire de base et une capacité identiques sur tous les runners. Si le runner journalise plutôt une ligne[runner:fatal]indiquant que la vérification du répertoire de base a expiré, le répertoire se trouve sur un montage NFS ou CSI bloqué. Vérifiez l’état du montage plutôt que les permissions. Le runner affiche ces deux échecs de démarrage sur stderr avant d’ouvrir--log-file; cherchez-les donc dans le terminal ou dans les logs de conteneur de votre plateforme plutôt que dans le fichier de log. Avant la v2.1.225, le runner ne vérifiait pas le répertoire de base au démarrage, et cette erreur de configuration faisait plutôt échouer les sessions après leur prise en charge. -
Le runner s’arrête au démarrage avec
RegisterRunner failed:et une erreur de certificat TLS : l’erreur peut être, par exemple,self signed certificate in certificate chain. Le runner ne fait pas confiance à l’autorité de certification (CA) à l’origine du certificat qu’il a reçu, comme c’est le cas derrière un proxy d’inspection TLS. DéfinissezNODE_EXTRA_CA_CERTSdans l’environnement du runner avec un fichier PEM contenant la CA racine du proxy. Vous n’avez pas à la redéfinir pour les sessions, qui en héritent. Git ne la lit pas : pour les clones passant par ce proxy, définissez égalementGIT_SSL_CAINFO. -
Votre script de démarrage affiche une erreur d’écriture contenant
Resource temporarily unavailableet s’arrête : votre script partage son stdout ou son stderr avec le processusclaude self-hosted-runnerou orchestrateur qu’il démarre, lequel a basculé ce pipe ou ce socket partagé en mode non bloquant. Lorsque le pipe est plein, une écriture de votre script échoue avec cette erreur du système d’exploitation au lieu d’attendre, et avecset -ele script s’arrête. Pour éliminer l’erreur, envoyez la sortie du runner ou de l’orchestrateur vers un fichier que l’utilisateur de votre script peut créer. Ajoutez>> "$HOME/claude-runner.log" 2>&1à la commande qui le démarre, avant tout&qui le place en arrière-plan :La sortie propre de votre script continue d’aller là où elle allait auparavant, tandis que ce que le runner écrit sur stdout et stderr, y compris ses erreurs de démarrage, va dans ce fichier. Utilisez cette redirection shell plutôt que--log-fileseul, qui duplique le log dans un fichier pendant que le runner continue d’écrire sur stdout et stderr. Si vous passez déjà--log-file, conservez-le également, car la redirection ne modifie pas ce que reçoit ce fichier. -
L’orchestrateur journalise
poll failed:avec une erreur de certificat TLS et continue de réessayer : l’orchestrateur ne fait pas confiance à la CA à l’origine du certificat qu’il a reçu, comme c’est le cas derrière un proxy d’inspection TLS. DéfinissezNODE_EXTRA_CA_CERTSdans l’environnement de l’orchestrateur avec un fichier PEM contenant la CA racine du proxy. Définissez-la également pour les runners que votre hookspawn-runnerdémarre. Par exemple, une ligne peut se terminer parpoll failed: self signed certificate in certificate chain — retrying in 1s, avec le message de la bibliothèque TLS entre les deux-points et le tiret. Pour confirmer la correction, vérifiez queconnectedvauttruedans le corps de/healthzde l’orchestrateur. -
Les sessions restent en file d’attente : chaque runner en ligne est peut-être verrouillé sur un propriétaire différent. Consultez la métrique
claude_code_self_hosted_runner_locked_accountde chaque runner ou le champlocked_accountde sa ligne de log[runner:health]pour savoir qui le détient. Les deux n’affichent l’adresse e-mail du propriétaire qu’une fois que le runner a reçu un jeton de session portant une revendicationact.email, ce qui n’est jamais le cas des sessions d’un agent Claude Tag. Sans cette revendication, le runner n’émet aucune sérielocked_accountet journaliselocked_account=yes, ce qui vous indique que le runner est verrouillé, mais pas sur quel propriétaire. Ajoutez des réplicas ou attendez qu’un runner existant se vide et redémarre. Si l’environnement utilise des runners à la demande, vérifiez plutôt l’orchestrateur ; consultez Runners à la demande. -
Les sessions échouent immédiatement après leur prise en charge : ouvrez la session dans claude.ai/code pour voir l’erreur. Si l’erreur indique que git n’a pas pu s’authentifier, vérifiez les identifiants git du runner. Sur un runner démarré avec
--use-anthropic-git-proxy, consultez Lorsque les sessions ne démarrent pas sur un runner avec le proxy git. Un répertoire de base non accessible en écriture arrête le runner au démarrage au lieu de faire échouer les sessions. Consultez l’entrée Le runner s’arrête au démarrage aveccannot create or write to base directoryde cette liste. -
Une session est prise en charge, mais Claude ne répond jamais à son premier message : si vos runners joignent
api.anthropic.comvia un proxy, configurez-le pour qu’il transmette la réponse/worker/events/streamde la session au fur et à mesure de son arrivée. Augmenter les délais d’expiration du runner n’aide pas. Pour confirmer la cause dans le log du runner, consultez Une session est prise en charge, mais Claude ne répond jamais à son premier message. -
Les sessions ne démarrent pas sur un runner ayant défini
--use-anthropic-git-proxy: cherchez dans le log du runneraccess denied by the git proxy, ou une erreur git mentionnant une adresseapi.anthropic.comcontenant/git_proxy/. Pour déterminer si Anthropic a servi la session et corriger la cause, consultez Lorsque les sessions ne démarrent pas sur un runner avec le proxy git. -
Les sessions ne peuvent pas accéder au réseau via un proxy de sortie avec authentification : lorsque la source que vous avez définie avec
--proxy-authorization-commandou--proxy-authorization-fileéchoue, expire au bout de 30 secondes ou renvoie une valeur vide, le runner répond502 Bad Gatewayà cette connexion et journalise la raison. Le runner masque le stderr de la commande dans ce log et ne journalise jamais la valeur de l’en-tête. Avec--proxy-authorization-command, exécutez vous-même la commande sur l’hôte pour vérifier qu’elle affiche la valeur complète de l’en-tête sur stdout. Si le runner s’arrête plutôt au démarrage aveccould not start the proxy-authorization listener, il n’a pas pu ouvrir son listener de loopback. -
Le runner journalise des lignes
Poll failedcontenantrejecting the malformed poll response: le runner a reçu une réponse d’interrogation de travail dont le corps n’est pas le JSON attendu de la file d’attente. L’une des causes possibles est un proxy d’interception ou un portail captif qui répond avec sa propre page. Si le runner se connecte via un proxy, configurez le proxy pour qu’il transmette sans modification les réponses deapi.anthropic.com. Le runner rejette la réponse malformée, la comptabilise sous le typetransportde la métriqueclaude_code_self_hosted_runner_poll_errors_totalet réessaie selon le calendrier des interrogations échouées décrit dans Cycle de vie des sessions. Le runner continue de servir ses sessions actives. Avant la v2.1.246, le runner interprétait une telle réponse comme une file de travail vide, ce qui pouvait mettre fin à ses sessions actives ou provoquer son arrêt. -
Une session échoue avec
no longer exists on the remoteoucouldn't find remote ref: le runner interprète le messagecouldn't find remote refde git comme une branche supprimée. La branche peut pourtant toujours exister. Cherchez-la sur votre hôte git, sous le nom exact figurant dans le message, puis vérifiez chaque cause qui correspond :- La branche a disparu : restaurez-la, comme le demande le message.
- La branche existe et vous n’administrez pas le runner : envoyez le message complet à l’administrateur du runner et indiquez-lui que la branche existe.
- La branche existe et le runner dispose d’un hook
checkout: une commande git de votre hook peut affichercouldn't find remote refsur stderr avant que le hook ne se termine avec un code non nul. Trouvez cette commande, et corrigez la ref qu’elle demande ou le dépôt distant depuis lequel elle effectue le fetch. Le runner transmet à votre hook la révision demandée par la session viaCLAUDE_RUNNER_REPO_REF. - La branche existe et le runner n’a pas de hook
checkout: le runner récupère lui-même la branche. Si un--git-host-rewrites’applique au dépôt, vérifiez que l’hôte vers lequel il réécrit possède la branche sous le même nom, et poussez-y la branche si ce n’est pas le cas. - La branche existe et le runner est antérieur à la v2.1.301 : mettez à niveau le runner comme décrit dans Épingler la version, afin qu’un autre échec affiche sa propre erreur. Avant la v2.1.301, d’autres échecs dont la sortie d’erreur contenait
couldn't find remote refétaient également signalés comme une branche supprimée.
-
Claude Tag ne publie parfois pas sa réponse dans Slack : le rappel de réponse est un hook fourni par le plan de contrôle. Vérifiez avec
claude --versionque le runner exécute Claude Code v2.1.229 ou ultérieure, car les runners plus anciens ignorent les hooks fournis ; vérifiez quepython3 --versionréussit dans l’image du runner, car les hooks exécutentpython3; et vérifiez qu’aucun fichier de paramètres ne désactive les hooks, qu’il s’agisse dedisableAllHooksdansmanaged-settings.json, dans le~/.claude/settings.jsonde l’utilisateur du runner, ou dans le.claude/settings.jsonou le.claude/settings.local.jsondu dépôt, ou encore deallowManagedHooksOnlyou d’unstrictPluginOnlyCustomizationcouvrant les hooks dans les paramètres gérés. -
Une session démarre sans l’un de ses dépôts : sur un runner sans hook
checkout, l’hôte git peut refuser la vérification d’accès du runner pour un dépôt que la session ne fait que lire. Le runner ignore alors ce dépôt, journalise une ligne[runner:warn] could not access context sourceindiquant le refus, et démarre la session avec les dépôts restants. Le runner n’ignore qu’un refus explicite : l’hôte répond que le dépôt est introuvable, git ne trouve aucun identifiant pour l’hôte, ou l’authentification échoue. Une défaillance réseau, un délai d’expiration ou un HTTP403fait toujours échouer le démarrage de la session, tout comme un refus pour un dépôt vers lequel la session pousse ses résultats. Le runner fait toujours échouer une session qui se retrouverait sans aucun dépôt si les dépôts refusés étaient ignorés. Avec--use-anthropic-git-proxy, le runner n’ignore qu’un dépôt que le proxy git lui-même refuse. La vérification d’accès est réexécutée chaque fois que la session démarre sur un runner ; ainsi, dès que l’identité git du runner dispose d’un accès en lecture, le démarrage suivant clone le dépôt. Avant la v2.1.274, chacun de ces refus faisait échouer le démarrage de la session. -
Une session échoue au démarrage avec
This runner couldn't read: le runner a demandé un dépôt à l’hôte git avec ses identifiants git et a reçu une réponse indiquant que le dépôt est introuvable. Le dépôt n’existe pas, ou ces identifiants ne permettent pas d’atteindre le dépôt.- Si vous administrez le runner : accordez à ses identifiants git un accès en lecture au dépôt, puis démarrez une nouvelle session.
- Si vous avez démarré la session : si le dépôt n’existe pas, démarrez une nouvelle session avec le bon dépôt. Si le runner ne peut pas atteindre le dépôt, demandez à l’administrateur du runner d’accorder l’accès, puis démarrez une nouvelle session.
-
Les sessions mettent plusieurs minutes à démarrer ou à reprendre : l’une des causes est un clone initial. Sur une flotte qui démarre chaque runner sur un disque vide, sans hook
checkout, le runner clone à nouveau les dépôts de la session avant de démarrer Claude Code. Une session qui reprend sur un disque vide attend de nouveau ce même clone. Pour éviter le clone initial, fournissez un checkout préchauffé. Si vous en fournissez déjà un, vérifiez que le runner l’utilise. Lorsque le runner effectue lui-même le clone et s’arrête après une seule session, réduisezCLAUDE_RUNNER_FETCH_DEPTHpar rapport à sa valeur par défaut de 50 commits afin de récupérer moins d’historique. Si vous utilisez un hook, il s’exécute à la place du clone du runner ; examinez donc plutôt ce que fait votre hook. Surveillez la métriqueclaude_code_self_hosted_runner_session_init_duration_secondspour connaître la durée des démarrages. -
Les tours échouent avec une erreur 401 : lorsqu’un tour se termine par une erreur 401 ou 403 de l’API Anthropic, le runner récupère un nouveau
CLAUDE_CODE_OAUTH_TOKENauprès d’Anthropic et le transmet à la session. Le tour échoué n’est pas réessayé. Ce jeton a une durée de vie courte, et le runner le renouvelle via le stdin de la session. Lorsqu’une récupération échoue, le runner journalise une ligneinference_token refresh failedindiquant quand il réessaiera, et il continue de réessayer tant que la session s’exécute. Si tous les appels commencent à échouer environ 30 minutes après le début d’une session, un script wrapper a probablement coupé le stdin de la session, de sorte que les renouvellements de jeton ne peuvent pas l’atteindre ; consultez Conserver stdin et le descripteur de fichier 3 attachés. Avant la v2.1.274, le runner cessait de réessayer une récupération échouée après quelques tentatives et attendait la suivante prévue. Un tour échoué ne déclenchait pas de récupération, si bien que chaque tour échouait avec une erreur 401 jusqu’à la récupération planifiée suivante. -
Un tour échoue dans claude.ai/code avec
This model needs a newer version of Claude Code: la version de Claude Code avec laquelle vos runners démarrent les sessions est plus ancienne que celle requise par le modèle de la session, si bien que l’API rejette la requête. Mettez à niveau les runners vers la version demandée par le message, ou une version ultérieure, comme décrit dans Épingler la version. D’ici là, les utilisateurs peuvent choisir un autre modèle dans la session. -
Le pod est tué en cours de vidage : augmentez
terminationGracePeriodSecondsà au moins la valeur que le runner journalise au démarrage. Consultez Délais d’arrêt.
[runner:fatal], sur stdout, et la sortie de débogage sur stderr, le tout sous forme de lignes de texte brut plutôt que de JSON. Les échecs liés au répertoire de base décrits dans les entrées de dépannage ci-dessus sont affichés sur stderr avant ce moment. Capturez les deux flux avec --log-file, qui permet également à self-hosted-runner doctor de les suivre, ou avec la collecte de logs de votre plateforme.
Le processus enfant de chaque session écrit un log de débogage distinct. En cas d’échec, le runner affiche la fin de ce log à côté de la session dans claude.ai/code. Sauf si vous avez démarré le runner avec --remove-session-state, il conserve également sur disque le log d’une session échouée et affiche son chemin dans le log du runner.
Lorsque le runner s’arrête
Ne redémarrez pas un runner à la demande, car son ordre de travail est à usage unique. Un runner qui s’arrête juste après avoir démarré doit être traité différemment d’un runner qui s’arrête pour toute autre raison.- Un arrêt normal : le runner a terminé ses sessions et s’est vidé, a atteint son heure de retrait ou a reçu l’ordre de s’arrêter. Redémarrez-le pour que l’environnement retrouve de la capacité. Cycle de vie du runner décrit ces arrêts.
- Un échec de démarrage : le runner ne peut pas démarrer avec la configuration ou l’hôte qui lui ont été fournis ; il s’arrête donc quelques secondes après avoir démarré, et il s’arrête de la même manière chaque fois que vous le redémarrez. Le redémarrer plus vite n’aide pas. Quelqu’un doit lire sa sortie et corriger la cause.
- Perte de contact : un runner qui ne peut pas joindre Anthropic pendant une durée supérieure à son bail, par exemple pendant la mise en veille de son hôte, peut être retiré de l’environnement. Lorsqu’un runner retiré se reconnecte, il s’arrête. Son log peut afficher une ligne
[runner:fatal]contenantrunner record gone server-sideou, après une interruption plus longue,poll auth failed. Le runner ne se réenregistre pas de lui-même ; redémarrez-le donc.
Reconnaître un échec de démarrage
Lorsque le runner ne peut pas démarrer, il affiche une ligne qui en indique la raison, puis il s’arrête. Pour la plupart des causes, la ligne contient[runner:fatal]. Pour certaines causes, la ligne commence plutôt par error:, notamment lorsque le runner ne peut pas analyser ses flags, ne peut pas lire le secret de l’environnement, ou ne peut pas créer le répertoire de base ni y écrire. La ligne suivante renvoie alors vers --help.
La plupart des lignes de log commencent par un horodatage et [self-hosted-runner], que l’exemple ci-dessous omet. Par exemple, un runner démarré avec le proxy git d’Anthropic et une capacité supérieure à un affiche une ligne comme celle-ci :
--log-file. Le runner affiche une ligne error: avant d’ouvrir le fichier de log ; cherchez-la donc dans le terminal ou dans vos logs de conteneur, comme l’indique Dépannage.
Ces éléments vous aident aussi à interpréter un échec de démarrage :
- Aucune ligne : un runner tué par l’hôte n’affiche ni l’une ni l’autre. Si la sortie se termine sans ligne
[runner:fatal]ni ligneerror:, vérifiez si l’hôte ou votre orchestrateur a arrêté le processus, par exemple pour dépassement d’une limite de mémoire. - Le code de sortie : le runner ne réserve pas de code de sortie aux erreurs qui se répètent à chaque démarrage. Il s’arrête avec le même code pour une erreur de configuration, comme une combinaison de flags non prise en charge, et pour un échec susceptible de se résoudre de lui-même, comme une API qui reste inaccessible malgré les nouvelles tentatives du runner. Fondez la décision d’attendre plus longtemps sur le délai au bout duquel le runner s’est arrêté, et lisez la sortie du runner pour en connaître la raison.
- Un environnement qui semble en bonne santé : certaines étapes de démarrage s’exécutent après l’enregistrement du runner auprès de votre environnement, comme
--configure-gitet la configuration des identifiants du proxy git d’Anthropic. Si l’une de ces étapes échoue, l’environnement peut continuer à répertorier ce runner pendant quelques minutes après l’arrêt du processus, et la page Cloud environments peut afficher Healthy alors qu’aucun runner ne prend de travail. Si les sessions restent en file d’attente dans un environnement qui semble en bonne santé, vérifiez si votre superviseur redémarre le runner.
Redémarrer avec une attente croissante
La manière d’obtenir une attente croissante dépend de votre superviseur.-
Kubernetes : le Deployment de cette page ne nécessite aucune modification. Après l’arrêt d’un conteneur, le kubelet attend par défaut avant de redémarrer le conteneur, et l’attente augmente à chaque redémarrage jusqu’à un plafond. L’attente repart de zéro une fois que le conteneur a fonctionné un certain temps sans s’arrêter.
Le kubelet applique la même attente après un arrêt normal lorsque le conteneur n’a fonctionné que brièvement. Un runner qui se vide souvent peut donc lui aussi afficher le statut
CrashLoopBackOff; lisez la sortie avant de conclure que le runner ne peut pas démarrer. La commande ci-dessous lit la sortie de la dernière exécution d’un pod du Deployment :Lorsque la dernière exécution était un échec de démarrage, la ligne[runner:fatal]ouerror:figure parmi les dernières lignes de la sortie. Pour lire la dernière exécution d’un autre pod, nommez ce pod à la place dedeploy/claude-runner. -
Docker et Docker Compose : la recette Compose de cette page ne nécessite aucune modification. Avec
restart: always, Docker attend plus longtemps avant chaque redémarrage d’un conteneur qui continue de s’arrêter, jusqu’à un plafond. Remplacez<container>par le nom du conteneur dans la commande ci-dessous, qui indique combien de fois Docker a redémarré le conteneur :La commande affiche un nombre. Un nombre qui ne cesse d’augmenter signifie que Docker continue de redémarrer le runner. -
Une unité systemd : par défaut, systemd attend le même
RestartSecavant chaque redémarrage et ne l’allonge pas ; une unité avecRestart=alwaysredémarre donc un runner qui ne peut pas démarrer à ce même intervalle à chaque fois. Lorsque les démarrages sont suffisamment rapprochés pour atteindre la limite de débit de démarrage de l’unité, cinq démarrages en 10 secondes par défaut, systemd cesse de redémarrer l’unité. L’unité reste arrêtée jusqu’à ce que quelqu’un la redémarre, ce que systemd autorise une fois l’intervalle de la limite de débit écoulé ou aprèssystemctl reset-failed. CommeRestartSecs’applique à chaque redémarrage, une valeur plus longue retarde également le redémarrage après un arrêt normal. Choisissez une valeur qui équilibre les deux, et configurez une alerte sur le nombre de redémarrages de l’unité. - Une boucle shell ou votre propre superviseur : appliquez vous-même la même règle. Commencez par une attente de cinq secondes. Après chaque exécution terminée en moins d’une minute, doublez l’attente avant le redémarrage suivant, jusqu’à cinq minutes. Après une exécution ayant duré une minute ou plus, revenez à cinq secondes.
Déterminer pourquoi le runner continue de s’arrêter
Lorsque le runner s’est arrêté juste après avoir démarré plusieurs fois de suite, arrêtez-vous et vérifiez les points suivants avant de le redémarrer à nouveau.- La dernière ligne
[runner:fatal]ouerror:: elle indique pourquoi le runner s’est arrêté. Dépannage répertorie les causes courantes. - La combinaison de flags : le proxy git d’Anthropic nécessite
--capacity 1. Les recettes de cette page utilisent une capacité plus élevée ; réduisez-la donc lorsque vous ajoutez le proxy à l’une d’elles. - Ce que l’environnement du service peut atteindre : si le runner démarre manuellement mais échoue sous votre superviseur, comparez l’utilisateur, le répertoire personnel, le
PATHet la limite de mémoire.--configure-gitet le proxy git d’Anthropic nécessitent git dans lePATHet un~/.gitconfigaccessible en écriture. - Le secret de l’environnement : si vous avez révoqué le secret ou fait une faute de frappe, le runner affiche une ligne contenant
RegisterRunner auth failed. - L’onglet Activity de l’environnement : ouvrez l’environnement et sélectionnez Activity. Si de nouveaux runners continuent d’y apparaître sans qu’aucun ne prenne de travail, votre superviseur redémarre le runner.
Une session est prise en charge, mais Claude ne répond jamais à son premier message
Si un runner prend en charge une session mais que Claude ne répond jamais au premier message, et que vos runners joignentapi.anthropic.com via un proxy, le proxy peut en être la cause. Anthropic envoie à une session ses messages via une unique réponse en streaming qui reste ouverte. Si votre proxy la met en tampon, ou y met fin avant d’avoir transmis quoi que ce soit, le premier message n’arrive jamais. Les autres requêtes de la session réussissent quand même.
Pour corriger le problème, modifiez la façon dont le proxy traite cette requête :
- Ne mettez pas la réponse en tampon : transmettez-la au fur et à mesure de son arrivée. Son type de contenu est
text/event-stream. - Laissez la réponse ouverte : elle peut rester ouverte jusqu’à une heure. Si le proxy a un délai d’inactivité, maintenez-le bien au-dessus de 15 secondes, car Anthropic envoie par défaut un keep-alive au moins aussi souvent.
Confirmer la cause dans le log du runner
Ces étapes concernent les runners sur Claude Code v2.1.287 ou ultérieure, avec le niveau par défaut--log-level info. Les runners plus anciens journalisent ces lignes plus tard, voire pas du tout.
- Attendez que
--startup-timeout-minsoit écoulé, 15 minutes par défaut. Le runner arrête alors la session et journalise une ligne qui contientno child output foret se termine par— releasing. - Cherchez
SDKStartup: SSEdans le log du runner. Ces lignes suivent une lignefinished: interrupted, quelques lignes après celle de l’étape 1. La plupart n’ont pas d’horodatage ; un collecteur de logs peut donc les afficher ailleurs. - Trouvez la ligne dans ce tableau :
Si le log ne contient aucune de ces lignes, ou si le log du proxy ne montre aucune anomalie, signalez un problème.
Empêcher la session de réessayer
La session conserve le message en attente, et un runner capable de servir le propriétaire de la session la reprend en charge. Tant que vous n’avez pas modifié le proxy, la session attend de la même manière à chaque fois. Pour y mettre fin, archivez la session dans claude.ai/code.Prochaines étapes
- Personnaliser les sessions : scripts wrapper, hooks de cycle de vie, runners à la demande, serveurs MCP, et permissions
- Tester de bout en bout : vérifier une nouvelle image runner à partir de CI avant de la promouvoir
- Référence : chaque drapeau CLI, variable d’environnement, et métrique