> ## Documentation Index
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Distribuisci ambienti self-hosted in produzione

> Esegui runner self-hosted in produzione: hardening della sicurezza, controllo dell'egress di rete, credenziali git, ricette Kubernetes e Compose, e risoluzione dei problemi.

> [!NOTE]
>
> Gli ambienti self-hosted sono in beta pubblica sui piani Team ed Enterprise; [Disponibilità e limitazioni](https://code.claude.com/docs/it/self-hosted-environments#availability-and-limitations) copre il percorso di abilitazione. Questa pagina copre l'esecuzione della flotta in produzione; consulta la [guida rapida](https://code.claude.com/docs/it/self-hosted-environments-quickstart.md) per il tuo primo runner e sessione.

Un [ambiente self-hosted](https://code.claude.com/docs/it/self-hosted-environments) esegue [sessioni cloud](https://code.claude.com/docs/it/claude-code-on-the-web.md) di Claude Code su runner che distribuisci all'interno della tua rete, e in produzione quelle sessioni eseguono codice diretto dal modello per conto di chiunque possa inviare una sessione all'ambiente. Questa pagina è per l'operatore che porta un ambiente funzionante in produzione. Funziona attraverso la distribuzione in ordine: cosa bloccare prima di connettere sistemi reali, l'egress di cui ha bisogno la flotta, come le sessioni si autenticano al tuo host git, le ricette di distribuzione stesse, e cosa controllare quando le sessioni si comportano male.

## Hardening della tua distribuzione

Un runner self-hosted esegue codice arbitrario diretto dal modello sulla tua infrastruttura per conto di chiunque possa inviare una sessione al suo ambiente. Questo è qualsiasi membro della tua organizzazione Anthropic, e chiunque possa avviare una sessione del canale [Claude Tag](https://claude.com/docs/claude-tag/overview) in un ambito che un Owner ha instradato all'ambiente. Lavora su ogni elemento prima di connettere un ambiente ai sistemi di produzione:

* **Container effimeri per sessione**: esegui ogni processo runner in un container o VM fresco che viene distrutto quando il processo esce, con `--capacity 1` e il valore predefinito `--drain-grace-sec 0` in modo che ogni container serva esattamente una sessione. A una capacità più alta, o con un drain grace positivo, un container serve più sessioni dallo stesso [owner bloccato](https://code.claude.com/docs/it/self-hosted-environments#key-concepts); vedi [Ciclo di vita del runner](https://code.claude.com/docs/it/self-hosted-environments#runner-lifecycle). Non riutilizzare un filesystem tra i riavvii del runner, tranne nella configurazione deliberata [pre-warmed checkout](#reuse-a-pre-warmed-checkout), e mai tra owner.
  * Quando il runner arresta una sessione, non invia alcun segnale a un processo ancora in esecuzione dopo che il suo comando shell è terminato, come un servizio che si è demonizzato. Distruggere il container o la VM termina quel processo.
* **Nessuna credenziale ampia nell'immagine**: non includere chiavi SSH di lunga durata, credenziali del provider cloud, o token di accesso personale che concedono più di quanto una sessione necessita. Crea credenziali utilizzate durante una sessione, come token push o API, per sessione dal tuo [script wrapper](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#wrapper-scripts). Il clone iniziale avviene prima che lo script wrapper venga eseguito, quindi gestiscilo con un [`checkout` lifecycle hook](https://code.claude.com/docs/it/self-hosted-environments-configuration#checkout), oppure con [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) quando tutti i repository di una sessione si trovano su github.com. Per entrambi, vedi [Configura git](#configure-git).
* **Mantieni le credenziali GitHub dell'host lontane dalle sessioni**: Claude può usare qualsiasi credenziale GitHub che una sessione può leggere, con qualunque accesso quella credenziale conceda. Tieni le credenziali GitHub con ambito ampio dell'host runner fuori da qualsiasi cosa una sessione possa leggere. Una tale credenziale può essere un token di accesso personale, il token che `gh auth login` salva per il tuo account, o un `GH_TOKEN` nell'ambiente del runner.
  * **Con [git gestito da Anthropic](#use-the-anthropic-git-proxy)**: con una tale credenziale, Claude raggiunge GitHub direttamente invece che tramite git gestito da Anthropic.
  * **Senza git gestito da Anthropic**: una credenziale di clone può restare nell'immagine se ne limiti l'ambito in modo rigoroso come descritto in [Includi la configurazione git nella tua immagine](#ship-git-config-in-your-image).
* **Mantieni il segreto dell'ambiente lontano dagli host che eseguono sessioni**: il segreto dell'ambiente può registrare runner e raccogliere qualsiasi sessione in coda sull'ambiente. Su una flotta fissa vive su ogni host runner, dove il codice di qualsiasi sessione può leggere il file segreto. Preferisci [runner on-demand](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#on-demand-runners), dove il segreto rimane sull'host dell'orchestrator, che non esegue mai codice utente, e ogni runner riceve un ordine di lavoro monouso che registra esattamente un runner. Su una flotta fissa, tratta il file environment-secret come leggibile da ogni sessione e ruota il segreto dopo qualsiasi sospetto compromesso della sessione.
* **Egress di rete default-deny**: limita il traffico in uscita del container runner e sessione al tuo confine di rete su ogni ambiente; [Default-deny egress](#default-deny-egress) copre cosa consentire e perché.
* **IAM host con privilegi minimi**: l'identità di calcolo allegata all'host runner, come un profilo di istanza o un account di servizio del nodo, dovrebbe concedere solo ciò di cui il runner stesso ha bisogno. Le sessioni dovrebbero ottenere le proprie credenziali attraverso il tuo script wrapper piuttosto che ereditare quelle dell'host.
* **Blocca l'endpoint dei metadati cloud dalle sessioni**: mantenere le sessioni fuori dall'identità dell'host richiede il blocco del loro accesso all'endpoint dei metadati cloud, e le politiche di egress a livello di subnet non intercettano il traffico dei metadati link-local, quindi bloccalo nel container stesso:

  * IMDSv2 con un hop limit di uno
  * GKE Workload Identity con metadata concealment
  * Un esplicito deny per `169.254.169.254` nello spazio dei nomi di rete del container della sessione

  Il blocco si applica anche al tuo script wrapper e ai lifecycle hook, poiché condividono il container. Autentica qualsiasi scambio di token con il [session JWT](https://code.claude.com/docs/it/self-hosted-environments-identity.md) contro il tuo servizio di token su egress allowlisted, o usa un'identità web basata su file come IAM Roles for Service Accounts (IRSA) su Amazon EKS.
* **Isolamento del filesystem per runner**: ogni processo runner ottiene la propria directory di lavoro che nessun altro processo sull'host può leggere o scrivere. Rendi `--hooks-dir`, lo script wrapper, e la `~/.claude/` dell'host di sola lettura per la sessione, sia incorporato nell'immagine che montato in sola lettura.
* **Dispatch non ha controllo di accesso per ambiente**: qualsiasi membro della tua organizzazione Anthropic può inviare una sessione a qualsiasi suo ambiente. Se un Owner [instrada i canali Claude Tag all'ambiente](https://code.claude.com/docs/it/cloud-environments.md#set-the-environment-a-claude-tag-channel-uses), chiunque l'[impostazione di accesso Claude Tag](https://claude.com/docs/claude-tag/admins/restrict-access#restrict-who-can-use-claude) ammette può avviare sessioni di canale che vengono eseguite lì. Per impostazione predefinita, questo è chiunque nel workspace Slack connesso, con o senza un account Claude. Tratta ogni host runner come raggiungibile per l'esecuzione di codice da parte di chiunque possa inviare a esso, e posiziona sull'host runner solo i dati e le credenziali che tutte quelle persone sono autorizzate a leggere. [`--lock-to-account`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) limita quale account le sessioni di un dato host eseguono, ma non restringe chi può inviare all'ambiente. Per rendere gli ambienti self-hosted l'unica opzione di selezione, un [Owner](https://code.claude.com/docs/it/cloud-environments.md#organization-shared-environments) può nascondere gli ambienti ospitati da Anthropic per l'intera organizzazione dalla pagina [**Cloud environments**](https://claude.ai/admin-settings/cloud-environments).
* **Applica la guardia repo-settings**: scegli la modalità di guardia con [`--confine-repo-settings`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags). Il valore predefinito `warn` registra una violazione e comunque genera la sessione, `enforce` rifiuta la sessione, e `off` disabilita la scansione. Il runner scansiona le impostazioni impegnate di ogni repository per:

  * Una concessione che si risolve al di fuori del workspace della sessione stessa: una voce `additionalDirectories`, una regola `Edit`, `Write`, o `NotebookEdit` in `permissions.allow`, o una voce `sandbox.filesystem.allowWrite` o `allowRead`
  * Un blocco `env` non vuoto
  * Un override della postura dell'operatore come `sandbox.enabled: false`

  La guardia viene eseguita indipendentemente da [`--trust-workspace`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags), e non copre gli hook del repository, `.mcp.json`, o le regole Bash; vedi [Permessi e approvazione degli strumenti](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#permissions-and-tool-approval) per dove quelle concessioni appartengono.

> [!NOTE]
>
> Se la tua organizzazione ha abilitato l'[allowlist IP](https://support.claude.com/en/articles/13200993-restrict-access-to-claude-with-ip-allowlisting), aggiungi gli indirizzi di egress pubblici dei tuoi runner e dei container di sessione all'allowlist prima di avviarli. Se esegui [runner on-demand](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#on-demand-runners), aggiungi anche l'indirizzo dell'host dell'orchestrator. Non fare affidamento sull'allowlist come controllo di rete per il traffico del runner o della sessione. Applica invece default-deny egress al tuo confine di rete.

## Requisiti di rete

Il runner e i figli della sessione che genera fanno connessioni in uscita agli host di seguito. Limita l'egress del container della sessione a questi host e ai servizi interni specifici che le sessioni devono raggiungere; [Default-deny egress](#default-deny-egress) copre come e perché.

Questi host sono sempre richiesti:

| Host | Porta | Utilizzato per |
| :- | :- | :- |
| `api.anthropic.com` | 443, HTTPS; WSS per il [git gestito da Anthropic](#use-the-anthropic-git-proxy) | Piano di controllo del runner e streaming della sessione, inferenza del modello, flag delle funzionalità, analitiche dei prodotti, recuperi della chiave [JWKS](https://code.claude.com/docs/it/self-hosted-environments-identity.md), firma dei commit e git gestito da Anthropic quando `--use-anthropic-git-proxy` è impostato |
| Il tuo host git, come `github.com` o il tuo host GitHub Enterprise | 443 o 22 | Clonazione e push dei repository su ogni host git usato dalle sessioni del runner. Su un runner che usa [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), vedi [quando il percorso `github.com` è ancora necessario](#github-com-egress-with-the-anthropic-git-proxy). |

Un runner che usa [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) instrada il suo traffico git verso `github.com` attraverso `api.anthropic.com`, quindi non ha bisogno del percorso dell'host git per `github.com`. Ne ha comunque bisogno se imposti `--push-outcome-on-release` o esegui il push da un hook `post-session`.

Se questi host sono necessari dipende dalla tua configurazione:

| Host | Porta | Quando richiesto |
| :- | :- | :- |
| `downloads.claude.ai` | 443 | Al momento dell'installazione, quando installi o aggiorni Claude Code sull'host con il programma di installazione nativo; lo script `install.sh` stesso viene servito da `claude.ai`. Al momento dell'esecuzione della sessione, solo quando le sessioni installano plugin dal marketplace ufficiale di Anthropic. |
| `storage.googleapis.com` | 443 | Al momento dell'esecuzione della sessione, per i conteggi di installazione dei plugin e i metadati mostrati in `/plugin`. |
| `code.claude.com` e `claude.com` | 443 | Ricerche di documentazione dall'agente claude-code-guide integrato e richieste WebFetch pre-approvate durante le sessioni. Il blocco di questi host influisce solo sulle ricerche di documentazione. |
| `*.frame.claudeusercontent.com` | 443 | Solo quando lo [strumento Artifact](https://code.claude.com/docs/it/artifacts.md#availability) è disponibile per le sessioni nella tua organizzazione; i valori predefiniti variano in base al piano, secondo la tabella di disponibilità lì. Imposta `CLAUDE_CODE_DISABLE_ARTIFACT=1` sul runner per mantenere lo strumento disabilitato indipendentemente dall'impostazione dell'organizzazione. |
| `registry.npmjs.org` | 443 | Quando una sessione installa un plugin, sia per il recupero dei pacchetti plugin da fonte npm che per l'installazione delle dipendenze Node.js di un plugin, o quando un server MCP lanciato da `npx` viene eseguito |
| `http-intake.logs.us5.datadoghq.com` | 443 | Metriche operative di Anthropic. Solo quando `CLAUDE_CODE_BYOC_ENABLE_DATADOG=1` è impostato; disabilitato per impostazione predefinita negli ambienti self-hosted. |
| `browser-intake-us5-datadoghq.com` | 443 | Caricamenti di rapporti di errore di Anthropic, inviati solo quando la [segnalazione di errori](https://code.claude.com/docs/it/data-usage.md#telemetry-services) è abilitata per l'account della sessione. Soppressa da `DISABLE_ERROR_REPORTING=1` o `DISABLE_TELEMETRY=1`. |
| Gli endpoint del tuo provider cloud per le richieste al modello, le ricerche dei modelli e il rinnovo delle credenziali, come `bedrock-runtime.us-east-1.amazonaws.com` o `aiplatform.googleapis.com` | 443 | Solo quando il runner [invia richieste al modello ad Amazon Bedrock o ad Agent Platform di Google Cloud](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform) |

Non è necessario aggiungere questi host all'allowlist per il traffico del runner o della sessione:

* **`statsig.anthropic.com`, `*.sentry.io`, `claude.ai` e `platform.claude.com`**: questi host appaiono in alcuni elenchi di controllo di rete enterprise più vecchi, ma il runner non li raggiunge. I recuperi dei flag delle funzionalità vanno a `api.anthropic.com`, e il runner si autentica con il segreto dell'ambiente piuttosto che con OAuth interattivo.
* **`mcp-proxy.anthropic.com`**: le sessioni self-hosted non lo usano. Quando la consegna dei connettori è abilitata per la tua organizzazione, i connettori claude.ai della tua organizzazione raggiungono le sessioni attraverso `api.anthropic.com`. Vedi [Server MCP](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#mcp-servers).

Questi flussi lato host raggiungono invece `claude.ai`, quindi eseguili da un host il cui egress lo consente piuttosto che ampliare l'egress del container della sessione:

* **Il programma di installazione a una riga**: recupera `install.sh` da `claude.ai` al momento dell'installazione.
* **`claude auth login` interattivo**: accede attraverso `claude.ai`, `claude.com` e `platform.claude.com`. La [configurazione guidata](https://code.claude.com/docs/it/self-hosted-environments-quickstart.md#run-the-guided-setup), la modalità con accesso di `doctor` e il [dispatch da CI](https://code.claude.com/docs/it/self-hosted-environments-testing.md#authenticate-from-ci) lo usano. Il browser con cui accedi carica anche i controlli del browser della pagina di accesso di claude.ai da `hcaptcha.com`, `*.hcaptcha.com` e `challenges.cloudflare.com`. Se la tua organizzazione usa il single sign-on, quel browser può anche essere reindirizzato attraverso `sso.claude.com`.

### Default-deny egress

Distribuisci container runner e sessione in un segmento di rete o namespace il cui traffico in uscita è limitato agli host nella [tabella dei requisiti di rete](#network-requirements), al tuo host git, e ai servizi interni specifici che le sessioni devono raggiungere. Il prodotto non può verificare o applicare questo, quindi applicalo al tuo confine di rete su ogni ambiente. Il codice della sessione è diretto dal modello e può tentare connessioni a host arbitrari; default-deny egress a livello di rete limita dove questi tentativi possono atterrare. Questo si applica indipendentemente dalla modalità di permesso: il set di strumenti pre-approvato predefinito include già `Bash`, quindi l'egress della shell viene eseguito senza un prompt anche senza [modalità auto](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#permissions-and-tool-approval).

Per i dettagli su quale telemetria ogni sessione emette e come disattivarla, vedi [Telemetria](https://code.claude.com/docs/it/self-hosted-environments-reference.md#telemetry).

### Autentica a un proxy di egress

Alcuni proxy di egress aziendali richiedono un'intestazione `Proxy-Authorization` su ogni connessione. Il token in quell'intestazione spesso ruota troppo velocemente per essere scritto nell'URL del proxy che imposti in `HTTPS_PROXY`. Imposta `HTTPS_PROXY` o `HTTP_PROXY` all'URL del tuo proxy come al solito, quindi imposta `--proxy-authorization-command` o `--proxy-authorization-file` per dire al runner dove leggere il valore dell'intestazione. Entrambi i flag richiedono Claude Code v2.1.238 o successivo.

#### Scegli da dove viene il valore `Proxy-Authorization`

Scegli il flag che corrisponde a come produci il token `Proxy-Authorization`:

* **[`--proxy-authorization-command <command>`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags)**: scegli questo per un token che generi su richiesta. Il runner esegue il comando della shell e usa il suo stdout ritagliato come valore dell'intestazione, ad esempio `Bearer <token>`.
* **[`--proxy-authorization-file <path>`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags)**: scegli questo per un token che un altro processo ruota in posizione. Il runner legge il file e usa i suoi contenuti ritagliati come valore dell'intestazione.

#### Configurazioni che il runner rifiuta di avviare con

Ogni flag ha anche una forma di variabile di ambiente, elencata accanto ad esso nel [riferimento dei flag CLI del runner](https://code.claude.com/docs/it/self-hosted-environments-reference.md#runner-cli-flags). Prima che il runner contatti il tuo proxy o il piano di controllo, controlla i flag e le loro variabili, e rifiuta di avviare in tre casi:

* **Entrambi i flag impostati**: un flag più la variabile di ambiente dell'altro flag conta come impostazione di entrambi.
* **Nessun URL proxy**: né `HTTPS_PROXY` né `HTTP_PROXY` contiene un URL `http://` o `https://`. Il runner legge entrambe le variabili in maiuscole o minuscole, e non consulta `ALL_PROXY`.
* **Uno dei flag passato al sottocomando orchestrator**: `self-hosted-runner orchestrator` non accetta i flag o le loro variabili di ambiente. Passa il flag a ogni runner che l'orchestrator avvia invece.

#### Cosa cambia il runner mentre un flag proxy-authorization è impostato

Con uno dei flag impostati, il runner avvia un listener proprio e invia il traffico proxy da se stesso, dai suoi lifecycle hook, e dalle sue sessioni attraverso quel listener. Il listener aggiunge l'intestazione `Proxy-Authorization` sulla strada verso il tuo proxy.

* **Listener**: il listener è un proxy forward su `127.0.0.1`. Il runner avvia il listener prima di registrarsi con il piano di controllo, e esce all'avvio se il listener non può avviarsi.
* **Variabili proxy**: il runner riscrive quale di `HTTPS_PROXY` e `HTTP_PROXY` hai impostato in modo che punti al listener. Quel valore riscritto raggiunge il runner stesso, i suoi lifecycle hook, e ogni sessione che esegue.
* **Rotazione del token**: un token ruotato ha effetto senza un riavvio. Per ogni connessione che il listener apre al tuo proxy, il runner esegue il tuo comando o legge di nuovo il tuo file e aggiunge il risultato come intestazione.
* **Ambiente della sessione**: una sessione raggiunge il tuo proxy solo attraverso il listener. Nell'ambiente di ogni sessione il runner rimuove `ALL_PROXY`, rimuove qualsiasi ortografia di `HTTPS_PROXY` o `HTTP_PROXY` che non hai impostato, e fissa `NO_PROXY` al valore del runner stesso.
* **Log**: il runner non registra mai il valore dell'intestazione.

## Configura git

Il runner gestisce i checkout dei repository ma non configura l'identità git o le credenziali per impostazione predefinita. Controlli l'immagine e l'ambiente del processo del runner, quindi controlli la configurazione git. Scegli uno di due approcci:

* **Lascia che il runner configuri git**: avvia il runner con `--configure-git` per fargli scrivere la stessa identità e configurazione di firma dei commit che usano le sessioni ospitate da Anthropic
* **Spedisci la configurazione git nella tua immagine**: imposta l'identità e le credenziali push tu stesso, ad esempio per eseguire il commit sotto la tua identità bot

Per i repository su github.com, puoi anche avviare il runner con [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), o impostare `CLAUDE_RUNNER_USE_GIT_PROXY=1`, per chiedere ad Anthropic di servire git per le sessioni del runner.

Piani minimi di versione git sull'host runner: [`--configure-git`](#let-the-runner-configure-git) la firma dei commit SSH richiede Git 2.34 o più recente, [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) richiede 2.32 o più recente, e la ripresa delle sessioni da rami spinti da [`--push-outcome-on-release`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) richiede 2.29 o più recente. Git 2.24 è sufficiente se ometti tutti e tre e gestisci l'identità git tu stesso.

### Lascia che il runner configuri git

Avvia il runner con `--configure-git`, o imposta `SELF_HOSTED_RUNNER_CONFIGURE_GIT=1`, per fargli scrivere la configurazione git globale all'avvio:

* `user.name = Claude` e `user.email = noreply@anthropic.com`, corrispondendo alle sessioni ospitate da Anthropic
* Firma dei commit e dei tag in formato SSH, instradata attraverso uno shim gestito dal runner che firma ogni commit tramite il servizio di firma di Anthropic usando le credenziali della sessione stessa. Le firme sono verificabili su GitHub rispetto alla chiave di firma SSH pubblicata di Anthropic.
* `push.negotiate = true`, in modo che git chieda al tuo host git quali commit ha già prima di impacchettare un push. Richiede Claude Code v2.1.257 o successivo.
* `core.hooksPath` che punta a una directory di hook gestita dal runner. I suoi hook `commit-msg` e `prepare-commit-msg` aggiungono a ogni commit un trailer `Co-authored-by:` per il creatore della sessione. Il trailer è costruito dall'email in [`CCR_SESSION_ACCOUNT_EMAIL`](https://code.claude.com/docs/it/self-hosted-environments-configuration#wrapper-scripts) e omesso quando quella variabile non è impostata. Se la tua immagine imposta già `core.hooksPath` e il runner non usa [git gestito da Anthropic](#use-the-anthropic-git-proxy), il runner mantiene la tua impostazione, salta l'installazione di questi hook e stampa un avviso `[runner:git]`.

La firma dei commit richiede git 2.34 o più recente; il runner controlla all'avvio e esce con un errore se il tuo git è più vecchio. Questo flag non configura le credenziali push, che fornisci comunque nell'immagine.

Su un runner con v2.1.280 o successivo, anche i commit che fai da un lifecycle hook `checkout` o `post-session` vengono firmati come la sessione, senza il trailer `Co-authored-by:`. [Configurazione git all'interno dei lifecycle hook](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#git-configuration-inside-lifecycle-hooks) descrive le impostazioni git che il runner fissa all'interno di quegli hook.

Con o senza `--configure-git`, Claude Code istruisce Claude a terminare i suoi messaggi di commit con un trailer `Claude-Session: <url>` e le descrizioni delle sue pull request con l'URL della sessione. Per ometterli entrambi, imposta [`attribution.sessionUrl`](https://code.claude.com/docs/it/settings-reference#attribution-sessionurl) su `false` nel file [`~/.claude/settings.json`](https://code.claude.com/docs/it/self-hosted-environments-configuration#how-each-session’s-config-is-assembled) dell'host runner, quindi riavvia il runner.

### Spedisci la configurazione git nella tua immagine

L'identità git è richiesta per qualsiasi commit. Impostala a livello di sistema nel tuo Dockerfile in modo che la configurazione si applichi indipendentemente da quale utente il processo runner esegue:

```dockerfile theme={null}
RUN git config --system user.name "Claude" && \
    git config --system user.email "noreply@anthropic.com"
```

Senza un'identità, `git commit` fallisce con `Please tell me who you are` e le sessioni non possono fare progressi. Puoi usare la tua identità bot invece; il runner non sovrascrive questi valori.

Non incorporare credenziali push di lunga durata o ampiamente scoped in un'immagine runner condivisa: una credenziale nell'immagine è disponibile a ogni sessione che l'immagine esegue, chiunque l'abbia avviata. Invece, crea un token a breve durata, con scope minimo per sessione dal tuo [script wrapper](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#wrapper-scripts), usando l'identità del creatore della sessione decodificata dal session JWT. Abbinalo a un container per sessione effimero, che richiede `--capacity 1`, in modo che nessuna credenziale sopravviva alla sessione che l'ha creata; vedi la [sezione hardening](#harden-your-deployment).

Se devi configurare le credenziali push a livello di immagine, ad esempio per una chiave di distribuzione di sola lettura, limitale il più possibile:

* Una chiave di distribuzione SSH limitata a un repository con una riscrittura `url.<base>.insteadOf`
* Un `credential.helper` che restituisce un token con scope minimo
* `GIT_SSH_COMMAND` che punta a una chiave con scope ristretto

Qualsiasi meccanismo tu configuri deve funzionare senza un prompt, perché il clone integrato del runner e il fetch disabilitano i prompt che git, SSH, e Git Credential Manager mostrerebbero altrimenti:

* Il runner imposta `GIT_TERMINAL_PROMPT=0`, in modo che git non chieda un nome utente o una password.
* Il runner esegue SSH con `BatchMode=yes`, aggiunto al tuo `GIT_SSH_COMMAND` se ne imposti uno, in modo che SSH non chieda una passphrase o una conferma dell'host.
* Il runner imposta `GCM_INTERACTIVE=never`, in modo che Git Credential Manager non apra una finestra di dialogo di accesso.
* Il runner cancella `core.askPass`, quindi se usi un helper askpass, impostalo attraverso la variabile di ambiente `GIT_ASKPASS` invece.

Se il tuo host git rifiuta la credenziale, o non ne hai configurata una, il runner riprova alcune volte e poi fallisce la preparazione del repository quando il repository è quello in cui la sessione spinge i risultati. Per un repository dal quale la sessione legge solo, [Troubleshooting](#troubleshooting) copre quando il runner lo salta invece. Il runner non passa queste impostazioni nell'ambiente della sessione.

Mantieni qualsiasi programma che nomini in `GIT_SSH_COMMAND` o `GIT_ASKPASS` dove le sessioni non possono scrivere, come la [checklist hardening](#harden-your-deployment) chiede per la directory dei hook e lo script wrapper. Lo stesso vale per qualsiasi chiave o file sulla riga di comando di quel programma. Il git del runner stesso esegue quel programma quando clona o recupera.

Se le directory di checkout sono di proprietà di un uid diverso dal processo runner, git rifiuta di operare su di esse; aggiungi `safe.directory`:

```dockerfile theme={null}
RUN git config --system --add safe.directory '*'
```

### Usa il proxy git di Anthropic

Con il proxy git di Anthropic, chiamato anche git gestito da Anthropic, l'immagine del runner non ha bisogno di chiavi SSH, credential helper, `.netrc` o altre credenziali git per la sessione stessa. Invece, il runner chiede ad Anthropic di servire git per le sue sessioni. Per una sessione utente servita da Anthropic, il clone del runner e i fetch e i push della sessione stessa passano attraverso Anthropic, che usa il token OAuth di GitHub memorizzato per il creatore della sessione. [Come Anthropic serve git per una sessione](#how-anthropic-serves-git-for-a-session) tratta le sessioni bot e agente.

Il proxy git è disattivato a meno che tu non lo [attivi](#turn-the-anthropic-git-proxy-on). Un runner che raggiunge il tuo host git con le proprie credenziali non ne ha bisogno, e il suo git funziona con qualsiasi host git.

In cambio, il proxy git limita ciò che il runner supporta e cambia ciò di cui ha bisogno:

* **Solo github.com**: Anthropic serve una sessione solo quando tutti i suoi repository sono su github.com, e il proxy git non supporta ancora GitHub Enterprise Server. Su un runner con il proxy git, una sessione con un repository su un altro host git [non riesce ad avviarsi](#when-anthropic-doesnt-serve-a-session).
* **Credenziali solo per i repository della sessione**: Anthropic fornisce le credenziali git per i repository che fanno parte della sessione, non per altri repository sullo stesso host git. Un submodule privato, una dipendenza che il tuo package manager recupera con git o un marketplace di plugin in un altro repository non ricevono alcuna credenziale da Anthropic. Chiedi alle persone che creano le sessioni di [aggiungere ogni repository](https://code.claude.com/docs/it/web-quickstart.md#start-a-task) di cui una sessione ha bisogno al momento della creazione.

  Ogni esecuzione di un'[attività pianificata nel cloud](https://code.claude.com/docs/it/routines.md) è una sessione con i repository dell'attività, quindi chiedi al proprietario di ciascuna attività di [aggiungere ogni repository di cui le sue esecuzioni hanno bisogno](https://code.claude.com/docs/it/routines.md#edit-and-control-routines).
* **Solo push di branch**: un push che elimina un branch fallisce, così come un push verso qualsiasi altro tipo di ref, come un tag. Per sapere quali branch un push può aggiornare, vedi [Proxy GitHub](https://code.claude.com/docs/it/cloud-environments.md#github-proxy).
* **Account GitHub collegati**: chiedi a ogni persona che crea sessioni utente con un repository di [collegare GitHub](https://code.claude.com/docs/it/web-quickstart.md#connect-github). Senza un proprio collegamento, quelle sessioni [non si avviano](#creator-has-no-github-connection).
* **`--capacity 1`**: il proxy git richiede una sessione per processo runner, quindi esegui più repliche per il parallelismo. [Attiva il proxy git di Anthropic](#turn-the-anthropic-git-proxy-on) elenca i requisiti.
* **Configurazione git globale sostituita**: il runner [elimina e sostituisce la configurazione git globale](#git-proxy-replaces-global-git-config) dell'utente con cui viene eseguito. Eseguilo come utente dedicato o in un container.
* **Credenziali dell'host per i push dell'host**: il push di [`--push-outcome-on-release`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) del runner e qualsiasi push effettuato dal tuo [hook `post-session`](https://code.claude.com/docs/it/self-hosted-environments-configuration#post-session) usano comunque le credenziali git dell'host runner e il suo [percorso di rete verso `github.com`](#github-com-egress-with-the-anthropic-git-proxy). Tieni quelle credenziali fuori dalla [configurazione git globale](#git-proxy-replaces-global-git-config) e vedi [Spedisci la configurazione git nella tua immagine](#ship-git-config-in-your-image) per i modi in cui fornirle.
* **Decisione per sessione**: Anthropic decide per ogni sessione sul runner se servire il suo git, e una sessione che non serve non riesce ad avviarsi. [Quando le sessioni non riescono ad avviarsi su un runner con il proxy git](#when-anthropic-doesnt-serve-a-session) ne illustra le cause.

> [!WARNING]
>
> Con `--use-anthropic-git-proxy` impostato, il runner elimina e sostituisce la configurazione git globale dell'utente con cui viene eseguito, senza conservarne un backup. Lo fa all'avvio e prima di ogni sessione. Un login o un credential helper che tenevi lì va perso. Le impostazioni scritte da [`--configure-git`](#let-the-runner-configure-git) vengono mantenute. Esegui il runner come utente dedicato o in un container, mai con il tuo utente.

Mantieni le impostazioni git non segrete, come l'identità e `safe.directory`, nella configurazione git di sistema.

#### Attiva il proxy git di Anthropic

Prima di avviare il runner con `--use-anthropic-git-proxy`, verifica che l'host runner soddisfi ciascuno di questi requisiti. Il runner rifiuta di avviarsi quando il requisito di capacità o quello di git non è soddisfatto:

* **Claude Code v2.1.267 o successivo**: le versioni precedenti accettano il flag ma non segnalano la richiesta ad Anthropic di servire git né stampano la riga `Registering as opted in`, quindi Anthropic non serve le loro sessioni.
* **`--capacity 1`, il valore predefinito**: ogni processo runner gestisce una sessione alla volta, quindi esegui più repliche per il parallelismo.
* **Git 2.32 o più recente**: git più vecchio ignora la configurazione git per sessione che il runner imposta per il proxy git.

> [!WARNING]
>
> Le ricette [Kubernetes](#kubernetes) e [Docker Compose](#docker-compose) su questa pagina usano `--capacity 4`. Se aggiungi `--use-anthropic-git-proxy` o `CLAUDE_RUNNER_USE_GIT_PROXY=1` a una di esse senza cambiare la capacità a `1`, il runner esce all'avvio ogni volta che il tuo orchestrator lo riavvia. Imposta `--capacity 1` ed esegui più repliche per il parallelismo. [Quando il runner esce](#when-the-runner-exits) mostra la riga che il runner stampa.

Per attivare il proxy git, aggiungi `--use-anthropic-git-proxy` al comando del runner, oppure imposta `CLAUDE_RUNNER_USE_GIT_PROXY=1` nell'ambiente del runner. Questo comando, eseguito in una shell sull'host runner, avvia il runner della [guida rapida](https://code.claude.com/docs/it/self-hosted-environments-quickstart.md#set-up-manually) con il proxy git attivo:

```bash theme={null}
claude self-hosted-runner --environment-secret-file '/etc/claude/environment-secret' --base-dir '<writable-dir>' --use-anthropic-git-proxy
```

All'avvio, il runner stampa `Registering as opted in to Anthropic-managed git (--use-anthropic-git-proxy)`. Anthropic decide quindi per ogni sessione su quel runner se servire il suo git. Per ogni sessione che serve, il runner registra una riga `[runner:session]` contenente `governed git ACTIVE`. Se invece una sessione non riesce ad avviarsi, vedi [Quando le sessioni non riescono ad avviarsi su un runner con il proxy git](#when-anthropic-doesnt-serve-a-session).

#### Come Anthropic serve git per una sessione

Per una sessione servita da Anthropic, il clone del runner e i fetch e i push della sessione stessa passano attraverso Anthropic, autenticati con il token a breve durata della sessione stessa:

* **Sessioni utente**: Anthropic usa il token OAuth di GitHub memorizzato per il creatore della sessione.
* **Sessioni bot e agente**: Anthropic usa il token di installazione della GitHub App della tua organizzazione.
* **Riscritture degli URL**: `--git-host-rewrite` e `--git-ssh-rewrite` non hanno effetto su un repository servito dal proxy git.

#### Quando le sessioni non riescono ad avviarsi su un runner con il proxy git

Su un runner avviato con `--use-anthropic-git-proxy`, una sessione non riesce ad avviarsi quando Anthropic non serve il suo git. Cerca nel log del runner un errore git che nomina un indirizzo `api.anthropic.com` contenente `/git_proxy/`.

Per ogni sessione, un runner con Claude Code v2.1.267 o successivo registra anche una riga `[runner:session]` contenente `governed git ACTIVE` quando Anthropic serve il git della sessione, oppure una riga `[runner:warn]` contenente `the server withheld Anthropic-managed git for this session` quando non lo fa. Trova la riga che vedi tra questi casi:

* **Né `governed git ACTIVE` né la riga `withheld`**: un runner precedente a Claude Code v2.1.267 non registra nessuna delle due righe, e Anthropic non serve le sue sessioni. Aggiorna il runner a v2.1.267 o successivo seguendo [Fissa la versione](#pin-the-version).
* **La riga `withheld`**: Anthropic non ha servito la sessione. Un runner che prima funzionava con il proxy git può fallire in questo modo senza alcuna modifica da parte tua.
  * **Un repository non è su github.com**: una sessione con anche un solo repository su un altro host git, come GitHub Enterprise Server, non viene servita, compresi i suoi repository su github.com. [Disattiva il proxy git di Anthropic](#turn-the-anthropic-git-proxy-off) per i runner di quell'ambiente.
  * **Tutti i repository sono su github.com**: segnala il problema al [tuo team di account Anthropic](#report-an-issue) con l'ID della sessione dalla riga `withheld`. Anthropic registra il motivo dal proprio lato.
* **Una riga contenente `remote: access denied by the git proxy`**: una sessione servita da Anthropic può comunque essere rifiutata, ad esempio quando la policy dell'organizzazione nega l'accesso git per la sessione, o la sessione non è autorizzata per il repository. Il log del runner mostra allora una riga contenente `remote: access denied by the git proxy`, e il resto di quella riga ne indica il motivo.
* **`GitHub authentication required`**: compare quando il creatore della sessione non ha una connessione GitHub funzionante su claude.ai. Il clone della sessione fallisce e l'errore git riporta `GitHub authentication required. Please reconnect your GitHub account.` Chiedi a quella persona di collegare o ricollegare GitHub nelle sue impostazioni di claude.ai.

Dopo aver corretto la causa, avvia di nuovo le sessioni non riuscite.

#### Disattiva il proxy git di Anthropic

Se le sessioni in un ambiente usano un repository su un host git diverso da github.com, come GitHub Enterprise Server, disattiva `--use-anthropic-git-proxy` per i runner di quell'ambiente.

**1. Rimuovi il flag**

Rimuovi `--use-anthropic-git-proxy` dal comando del runner. Se hai impostato `CLAUDE_RUNNER_USE_GIT_PROXY` nell'ambiente del runner, ad esempio in una pod spec o in un file Compose, rimuovila da lì. In una shell, annulla la sua impostazione:

```bash theme={null}
unset CLAUDE_RUNNER_USE_GIT_PROXY
```

**2. Fornisci al runner le credenziali git**

Fornisci credenziali che funzionino senza un prompt per ogni host git usato dalle sessioni dei runner, github.com compreso. Qualsiasi credenziale presente nella configurazione git globale dell'utente del runner non c'è più, perché il runner ha eliminato quella configurazione mentre `--use-anthropic-git-proxy` era impostato. [Spedisci le credenziali nella tua immagine](#ship-git-config-in-your-image) o usa un [lifecycle hook `checkout`](https://code.claude.com/docs/it/self-hosted-environments-configuration#checkout).

**3. Apri il percorso di rete**

Consenti al runner di raggiungere ogni host git usato dalle sessioni dei runner sulla porta 443 o 22. Vedi la riga dell'host git in [Requisiti di rete](#network-requirements).

**4. Riavvia i runner**

Riavvia i runner in modo che si registrino senza il proxy git. Quindi avvia di nuovo ogni sessione non riuscita.

#### Accesso all'API di GitHub senza la GitHub CLI

Se l'immagine del tuo runner non include la GitHub CLI, Claude Code può fornire un `gh` integrato, così Claude può comunque aprire pull request, commentare e leggere i risultati della CI. Il `gh` integrato è pensato per i runner che usano git gestito da Anthropic. Supporta un solo comando, `gh api`, che chiama la REST API di GitHub. Richiede Claude Code v2.1.287 o successivo nell'immagine del runner.

Questo comando apre una pull request al posto di `gh pr create`. Il `gh` integrato compila `{owner}` e `{repo}` per il repository corrente:

```bash theme={null}
gh api repos/{owner}/{repo}/pulls -f title='Fix' -f head='my-branch' -f base='main'
```

* **Credenziali**: il `gh` integrato invia le sue richieste REST attraverso git gestito da Anthropic, che fornisce la credenziale GitHub dal lato di Anthropic, quindi l'immagine non ha bisogno di alcun token GitHub per esso
* **Quali sessioni lo ottengono**: Anthropic decide per ogni sessione se git gestito da Anthropic serve il `gh` della sessione. Quando lo fa, la riga `[runner:session] governed git ACTIVE` che il runner registra per la sessione mostra `gh_path_shim=true`. Quando non lo fa, la sessione non ha `gh`
* **`jq`**: installa `jq` nell'immagine se vuoi che `--jq` funzioni
* **[`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](https://code.claude.com/docs/it/env-vars)**: se l'ambiente della sessione la imposta, Claude Code non fornisce il `gh` integrato e la sessione non ha `gh`

Quando l'immagine include la GitHub CLI, le sessioni usano quella.

#### Affida un'autorità di certificazione privata a git gestito da Anthropic

Questa sezione si applica se imposti `GIT_SSL_CAINFO` o `GIT_SSL_NO_VERIFY` nell'ambiente di un runner le cui sessioni usano git gestito da Anthropic. La gestione che descrive richiede che il runner esegua Claude Code v2.1.283 o successivo.

Quando git sul runner deve fidarsi di un'autorità di certificazione (CA) privata, come quella con cui un proxy che ispeziona TLS firma, gli approcci usuali funzionano come segue:

* **Archivio certificati di sistema**: installa la tua CA nell'archivio certificati di sistema dell'host runner, e git si fida di essa senza nessuna delle due variabili.
* **`GIT_SSL_CAINFO`**: impostala a un file PEM dei tuoi CA, ad esempio `GIT_SSL_CAINFO=/etc/ssl/corp-ca.pem`.
* **`GIT_SSL_NO_VERIFY`**: non aiuta dietro un proxy che firma di nuovo. Il clone del runner stesso attraverso git gestito da Anthropic controlla i certificati anche quando la variabile è impostata, quindi quel clone fallisce finché git non si fida della tua CA attraverso uno degli altri due approcci.

Per le connessioni git che portano il token di una sessione a git gestito da Anthropic, il runner applica le due variabili come segue. Un [`command` hook](https://code.claude.com/docs/it/self-hosted-environments-configuration#command) inizia con l'ambiente della sessione, quindi ottiene quello che git dentro la sessione ottiene:

* **`GIT_SSL_CAINFO`**: quello che git controlla git gestito da Anthropic dipende da dove git viene eseguito:
  * **Clone e fetch del runner stesso**: vengono eseguiti senza la variabile e controllano git gestito da Anthropic rispetto a un file di certificato per sessione che il runner scrive. Il runner crea quel file a partire dal bundle CA di sistema dell'host runner e dai certificati presenti nel tuo file.
  * **Git dentro la sessione**: ottiene la configurazione `http.sslCAInfo` che nomina il tuo file al posto della variabile, più voci `http.<url>.sslCAInfo` che controllano git gestito da Anthropic rispetto al file per sessione.
  * **Hook `checkout` e `post-session`**: ereditano la variabile invariata.
* **`GIT_SSL_NO_VERIFY`**: quali controlli di certificato rimangono spenti dipende da dove git viene eseguito:
  * **Clone e fetch del runner stesso**: vengono eseguiti senza la variabile e controllano il certificato che gli viene presentato.
  * **Git dentro la sessione**: ottiene la configurazione `http.sslVerify=false` al posto della variabile, quindi i controlli rimangono spenti per altri host. Ottiene anche voci `http.<url>.sslVerify=true` che mantengono i controlli accesi per git gestito da Anthropic.
  * **Hook `checkout` e `post-session`**: quando la sessione ha un repository su git gestito da Anthropic, ottengono la configurazione `http.sslVerify=false` al posto della variabile. Ottengono anche voci `http.<url>.sslVerify=true` che mantengono i controlli accesi per git gestito da Anthropic.

Il file di certificato per sessione ha bisogno di un bundle CA di sistema a `/etc/ssl/certs/ca-certificates.crt` o `/etc/pki/tls/certs/ca-bundle.crt` sull'host runner. Ha anche bisogno di un file `GIT_SSL_CAINFO` che l'utente del runner può leggere, che contiene blocchi PEM `CERTIFICATE`, e che è al massimo 1 MiB. Quando il runner non può costruire il file per sessione, registra una riga `[runner:warn]` contenente `did not build the certificate file` e il motivo. Git quindi usa il tuo file come è per git gestito da Anthropic. Correggi quello che la riga nomina.

Per ogni sessione che usa git gestito da Anthropic, il runner registra anche una riga `[runner:warn]` che inizia con `governed git: GIT_SSL_CAINFO is set` o `governed git: GIT_SSL_NO_VERIFY is set`. La riga dice quello che il runner ha fatto con quella variabile per il suo git, per git dentro la sessione, e per i tuoi lifecycle hook. Termina con se hai bisogno di cambiare qualcosa.

### Aggiungi repository github.com alle sessioni dei canali Claude Tag

Se Claude non riesce ad aggiungere un repository github.com a una sessione di un canale Claude Tag, assicurati che il canale vi abbia accesso, anche quando il tuo runner riesce a clonarlo. Quell'accesso deriva dalle impostazioni su GitHub e nella tua organizzazione Claude, non dalle credenziali git del tuo runner.

**1. Installa la Claude GitHub App**

Installa la [Claude GitHub App](https://github.com/apps/claude) sull'organizzazione o sull'account GitHub proprietario del repository e includi quel repository nell'installazione.

**2. Collega l'organizzazione o l'account GitHub**

In [**Organization settings > Git providers**](https://claude.ai/admin-settings/source-control), collega quell'organizzazione o quell'account alla tua organizzazione Claude. Può collegarla un Owner della tua organizzazione Claude che sia anche proprietario dell'organizzazione GitHub. Vedi [Collegare la tua organizzazione GitHub](https://claude.com/docs/claude-tag/admins/configure-github#link-your-github-organization).

**3. Concedi il repository**

Nelle [impostazioni di amministrazione di Claude Tag](https://claude.ai/admin-settings/claude-tag), concedi il repository al canale o a un ambito più ampio che includa il canale: il suo workspace Slack o tutto Slack. Può concederlo chi ha il ruolo Owner o il permesso Claude Tag Admin. Anche un gestore del canale può aggiungerlo al proprio canale se il suo account GitHub è amministratore del repository. Vedi [Concedere l'accesso ai repository](https://claude.com/docs/claude-tag/admins/configure-github#grant-repository-access).

Quindi avvia un nuovo thread nel canale e [nomina il repository](https://claude.com/docs/claude-tag/admins/configure-github#how-granted-repositories-reach-a-session) nel tuo primo messaggio.

Se Claude non riesce ancora ad aggiungere il repository, vedi [Se Claude non riesce a raggiungere un repository](https://claude.com/docs/claude-tag/admins/configure-github#if-claude-can%E2%80%99t-reach-a-repository) per sapere dove controllare il collegamento GitHub e la concessione del repository.

### Riscrivi gli URL git per le reti private

Gli URL dei repository arrivano dal piano di controllo come HTTPS, con il nome host del tuo host git; per GitHub Enterprise, questo è il nome host che hai configurato per l'[integrazione GitHub Enterprise](https://code.claude.com/docs/it/github-enterprise-server.md) su claude.ai. Due flag ripetibili riscrivono quegli URL prima del clone:

* `--git-host-rewrite <from>=<to>`: per split-horizon DNS, dove Anthropic raggiunge il tuo host git tramite un nome host esterno ma i runner devono usarne uno interno
* `--git-ssh-rewrite <host>`: per host git che accettano solo SSH, o per un host runner che ha solo una chiave SSH. Ad esempio, per clonare i repository di github.com tramite SSH, aggiungi `--git-ssh-rewrite github.com` al comando del runner. Il runner riscrive quindi `https://github.com/owner/repo` in `git@github.com:owner/repo`. SSH deve [connettersi senza un prompt](#git-must-work-without-a-prompt).

La riscrittura dell'host viene eseguita per prima, quindi elenca il nome host interno in `--git-ssh-rewrite` se hai bisogno di entrambi. Per il controllo completo del checkout, usa un [`checkout` lifecycle hook](https://code.claude.com/docs/it/self-hosted-environments-configuration#checkout).

## Costruisci l'immagine del runner

Anthropic non pubblica un'immagine runner pre-costruita. Costruisci la tua intorno al binario `claude`, stratificando qualsiasi toolchain di cui i tuoi repository hanno bisogno: runtime di linguaggio, compilatori, gestori di pacchetti, e sidecar [MCP](https://code.claude.com/docs/it/mcp.md).

Le ricette di seguito usano `--capacity 4`, in modo che un container serva fino a quattro sessioni concorrenti dallo stesso owner bloccato. Questo non fornisce l'isolamento del container per sessione nella [sezione hardening](#harden-your-deployment): prima di connettere un ambiente ai sistemi di produzione, esegui le ricette a `--capacity 1` con un container per sessione, o usa [runner on-demand](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#on-demand-runners), che mantengono anche il segreto dell'ambiente lontano dagli host che eseguono sessioni. Se aggiungi il [proxy git Anthropic](#use-the-anthropic-git-proxy) a una di queste ricette, cambia anche `--capacity` a `1`.

Questo Dockerfile è un punto di partenza minimo. Installa `python3` perché gli [hook che il control plane di Anthropic fornisce](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#how-each-session’s-config-is-assembled) alle sessioni Claude Tag, incluso quello che fa pubblicare a Claude la sua risposta in Slack, eseguono `python3`. Mantieni `python3` nel `PATH` in qualsiasi immagine che serve sessioni Claude Tag:

```dockerfile theme={null}
FROM debian:bookworm-slim
ARG CLAUDE_CODE_VERSION
RUN apt-get update && apt-get install -y --no-install-recommends git curl ca-certificates openssh-client jq python3 \
 && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL "https://downloads.claude.ai/claude-code-releases/${CLAUDE_CODE_VERSION:?set with --build-arg CLAUDE_CODE_VERSION}/linux-x64/claude" \
      -o /usr/local/bin/claude && chmod +x /usr/local/bin/claude
RUN git config --system user.name "Claude" \
 && git config --system user.email "noreply@anthropic.com" \
 && git config --system --add safe.directory '*'
ENTRYPOINT ["claude"]
```

Scambia `linux-x64` con `linux-arm64` se i tuoi nodi sono ARM, o con `linux-x64-musl` o `linux-arm64-musl` su un'immagine basata su musl come Alpine; vedi [Configurazione Alpine Linux](https://code.claude.com/docs/it/setup.md#alpine-linux-and-musl-based-distributions) per i pacchetti extra di cui le immagini musl hanno bisogno. L'URL è la posizione di rilascio standard di Claude Code, quindi puoi verificare il binario scaricato rispetto al manifesto firmato del rilascio come descritto in [Integrità binaria e firma del codice](https://code.claude.com/docs/it/setup.md#binary-integrity-and-code-signing). Il runner richiede Claude Code versione 2.1.224 o successiva, oppure 2.1.229 o successiva per servire sessioni Claude Tag. Esegui la build dell'immagine, quindi spingila al tuo registro e fai riferimento ad essa nelle ricette di seguito:

```bash theme={null}
docker build \
  --build-arg CLAUDE_CODE_VERSION="$(curl -fsSL https://downloads.claude.ai/claude-code-releases/stable)" \
  -t <your-registry>/claude-runner:latest .
```

La sostituzione di comando cerca il numero di rilascio `stable` corrente e lo passa come argomento di build, quindi eseguire lo stesso comando dopo un nuovo rilascio stabile ricostruisce il livello di download con il binario più recente. Per fissare un rilascio specifico per build riproducibili, passa il numero di versione direttamente come `CLAUDE_CODE_VERSION`. Sostituisci `stable` con `latest` nell'URL di ricerca quando hai bisogno di un rilascio più recente del canale stabile, come uno che [un modello appena lanciato richiede](https://code.claude.com/docs/it/model-config.md).

## Dimensiona CPU e memoria per le sessioni

Dimensiona il container o l'host di un runner per le sessioni che esegue piuttosto che per il processo runner. Il runner stesso esegue il polling per il lavoro, prepara il checkout di ogni sessione, esegue i tuoi [lifecycle hook](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#lifecycle-hooks), e avvia e supervisiona i processi della sessione. Il carico proviene dalle sessioni: ognuna è un processo Claude Code più tutto ciò che avvia, come build, suite di test, installazioni di pacchetti, e [server MCP](https://code.claude.com/docs/it/mcp.md).

Per una sessione, inizia con i seguenti valori, indicati come richieste e limiti di Kubernetes o l'equivalente della tua piattaforma, e trattali come un punto di partenza piuttosto che un requisito:

* **Memoria**: una richiesta e un limite di 4 GiB ciascuno, che soddisfa il minimo di 4 GB nei [requisiti di sistema](https://code.claude.com/docs/it/setup.md#system-requirements) di Claude Code. Mantieni i due uguali in modo che lo scheduler conti la memoria completa del container. Quando il container raggiunge il suo limite di memoria, il kernel uccide i processi al suo interno, il che può terminare una sessione a metà compito.
* **CPU**: una richiesta di 2 CPU e un limite di 4 CPU, in modo che una sessione possa scoppiare sopra la richiesta durante le build. Il kernel limita un container al suo limite di CPU piuttosto che uccidere i processi al suo interno, quindi le sessioni al limite vengono eseguite più lentamente ma continuano a funzionare.

In una specifica di container Kubernetes, imposta quei valori iniziali con il seguente blocco `resources`:

```yaml theme={null}
resources:
  requests:
    cpu: "2"
    memory: 4Gi
  limits:
    cpu: "4"
    memory: 4Gi
```

Le build e i test sono solitamente la parte più grande e più variabile del carico di una sessione, quindi esegui una build rappresentativa del tuo repository, misura il suo picco di CPU e memoria, e aumenta qualsiasi valore iniziale che non lascia spazio per il processo Claude Code in cima a quel picco.

Il runner usa `--capacity` per limitare quante sessioni esegue contemporaneamente. Non divide CPU o memoria tra di loro, quindi le sessioni su un runner condividono la CPU e la memoria del container. Per evitare che l'uso di memoria di una sessione termini le altre, esegui una sessione per runner con [il proprio limite di memoria](#give-each-session-its-own-memory-limit).

* **Una sessione per runner**: dai a ogni container i valori di una sessione. Usa questo dimensionamento a `--capacity 1`, che la [sezione hardening](#harden-your-deployment) consiglia, e per [runner on-demand](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#on-demand-runners), dove imposti i valori sul carico di lavoro che il tuo hook [`spawn-runner`](https://code.claude.com/docs/it/self-hosted-environments-configuration#the-spawn-runner-hook) invia, come il modello di pod di un Kubernetes Job.
* **Diverse sessioni per runner**: a un `--capacity` sopra uno, moltiplica i valori di una sessione per la capacità, perché fino a quel numero di sessioni possono essere eseguite nel container contemporaneamente. Le ricette [Kubernetes](#kubernetes) e [Docker Compose](#docker-compose) eseguono `--capacity 4` senza limiti di CPU o memoria, quindi aggiungi limiti dimensionati per la capacità che esegui.
* **Diversi container runner su un host**: dimensiona l'host per la somma delle capacità dei runner, perché ogni runner conta solo le proprie sessioni rispetto a `--capacity`:

  * **Memoria**: moltiplica la memoria di una sessione per la somma, quindi aggiungi ciò che usano i processi propri dell'host
  * **CPU**: moltiplica la richiesta di CPU di una sessione per la somma. Per permettere a ogni sessione di superare temporaneamente la richiesta contemporaneamente, moltiplica invece il suo limite di CPU.

  Dai a ogni runner il proprio container. Usa lo [stesso percorso `--base-dir`](#keep-the-base-directory-and-capacity-identical-across-runners) in ciascuno, su uno storage che nessun altro container runner monta, in modo che ogni runner abbia [la propria directory di lavoro](#harden-your-deployment).

### Dai a ogni sessione il proprio limite di memoria

Per evitare che una sessione termini le altre quando la memoria si esaurisce, esegui ogni runner a `--capacity 1` sotto il proprio limite di memoria, impostato sulla memoria di una sessione:

* **In un container**: esegui un runner per container e imposta il limite di memoria del container, come `limits.memory` su Kubernetes, `--memory` su Docker, o `mem_limit` in un file Compose. Mantieni la somma dei limiti dei container al di sotto della memoria dell'host, perché quando è l'host stesso a esaurirla, il kernel può uccidere processi ovunque sull'host.
* **Sotto systemd senza un container**: imposta `MemoryMax` sull'unità del runner ed esegui un runner per host, perché i runner che [usano lo stesso `--base-dir`](#keep-the-base-directory-and-capacity-identical-across-runners) su un filesystem condividono i repository clonati lì. Ad esempio, a `--capacity 1` senza hook `checkout`, le loro sessioni su un repository possono lavorare nello stesso checkout contemporaneamente. Per eseguire diversi runner su un host, dai a ciascuno un container.

Quando un container o un'unità raggiunge il proprio limite di memoria, il kernel uccide i processi solo al suo interno. Se imposti `--capacity` sopra uno, le sessioni condividono un unico limite. Quando lo raggiungono, il kernel non sceglie per sessione quali processi uccidere, quindi l'uso di memoria di una sessione può terminare le altre.

Su Kubernetes, non puoi separare le sessioni con nessuno dei due valori dell'impostazione `singleProcessOOMKill` del kubelet. Non puoi nemmeno limitare la memoria totale di una sessione con un `ulimit` di memoria nel tuo [script wrapper](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#wrapper-scripts). Il kernel applica un valore come `ulimit -v` a ogni processo separatamente.

### Lascia spazio sotto i limiti di processi e thread

Per dimensionare un limite di processi e thread, misura ciò di cui ha bisogno una sessione e moltiplicalo per il numero di sessioni che condividono il limite. Quando quelle sessioni raggiungono il limite, il kernel rifiuta nuovi processi e thread, quindi comandi, build e test in ognuna di esse possono fallire. La chiamata rifiutata restituisce `EAGAIN`, il cui messaggio standard è `Resource temporarily unavailable`.

Supponi che una sessione raggiunga un picco di 300 processi e thread, e che quattro sessioni condividano il limite. Insieme ne richiedono 1.200, e con un limite di 2.400 lasci spazio per build che ne richiedono il doppio.

#### Dove impostare ciascun limite

Ogni limite in questa tabella conta i thread oltre ai processi, e `pids.max` è il file che contiene il limite di un cgroup:

| Limite | Dove lo imposti | Cosa lo condivide |
| :- | :- | :- |
| `pids.max` su Kubernetes | `podPidsLimit` nella configurazione del kubelet dei nodi che eseguono i runner. Non puoi impostarlo in una specifica di Pod. | I container di un Pod |
| `pids.max` su Docker | `--pids-limit`, o `pids_limit` in un file Compose | Un container |
| `pids.max` sotto systemd | `TasksMax` sull'unità del runner | Un'unità |
| `ulimit -u` | La shell o il servizio che avvia il runner | Tutto ciò che sull'host viene eseguito con l'ID utente del runner. Non applicato per root. |

Quando non imposti `TasksMax`, systemd applica `DefaultTasksMax`, che per impostazione predefinita è il 15% del valore più basso tra `kernel.pid_max`, `kernel.threads-max` e il `pids.max` del cgroup root.

#### Misura il picco di una sessione

Per ottenere i numeri per i tuoi runner, misura una sessione sul suo host, che su Kubernetes è il nodo:

**1. Avvia un runner da solo**

Avvia un runner a `--capacity 1` senza nient'altro nel suo container o nella sua unità systemd, da solo sul suo host e nel suo ambiente:

* **Il suo host**: usane uno che non esegue nessun altro runner. Potrai così individuare il suo processo nel passaggio successivo.
* **Il suo ambiente**: rendilo l'unico runner lì, ad esempio in un ambiente che [crei per il test](https://code.claude.com/docs/it/self-hosted-environments-quickstart.md#set-up-manually). Le sessioni vengono instradate a un ambiente e non a un runner.

Non avviare ancora una sessione, perché ti serve la directory del passaggio successivo prima che la build inizi.

**2. Trova la directory cgroup del runner**

Sull'host, non all'interno del container, trova l'ID del processo del runner, ad esempio con `ps`. Leggi `/proc/<pid>/cgroup` per quell'ID:

* **Una riga contiene `:pids:`**: l'host usa cgroup v1, e la directory è `/sys/fs/cgroup/pids` seguita dal testo dopo `:pids:`
* **L'unica riga inizia con `0::`**: l'host usa cgroup v2, e la directory è `/sys/fs/cgroup` seguita dal testo dopo `0::`

**3. Esegui una build rappresentativa e registra il picco**

Esegui una sessione attraverso una build rappresentativa e rileva il picco dalla directory cgroup del runner:

* **La directory ha `pids.peak`**: leggi il file al termine della build
* **La directory non ha `pids.peak`**: leggi `pids.current` una volta al secondo mentre la build è in esecuzione e conserva il valore più alto. Un picco tra due letture passa inosservato, quindi considera quel valore come una stima per difetto.

**4. Moltiplica per le sessioni che condividono il limite**

* **Il Pod, il container o l'unità di un runner**: moltiplica il picco per il `--capacity` del runner
* **Un limite condiviso da diversi runner**: moltiplica il picco per la somma delle loro capacità

Poi aggiungi il picco di qualsiasi altra cosa che condivide il limite, come un altro container nel Pod.

**5. Imposta il limite al di sopra del risultato**

Con un limite più alto lasci spazio per build più pesanti, e con uno più basso fermi prima una fork bomb.

#### Controlla un limite `pids.max`

* **Verifica se `pids.max` è stato raggiunto**: cerca nel log del kernel dell'host, ad esempio con `dmesg`, una riga che contiene `cgroup: fork rejected by pids controller in` e il percorso di un cgroup. Il kernel lo registra solo la prima volta che rifiuta un processo o un thread in quel cgroup, quindi cerca nell'intero log.
* **Visualizza il limite in vigore**: leggi `pids.max` nella directory cgroup del runner e in ogni directory al di sopra di essa. Il valore più basso è il limite in vigore, e `max` significa nessun limite a quel livello.

## Kubernetes

Il runner serve `GET /healthz` sulla porta 8080 per impostazione predefinita, configurabile con `--health-port`, quindi i probe di Kubernetes funzionano senza configurazione extra. L'endpoint restituisce `200` ogni volta che il processo è vivo, quindi i probe di seguito rilevano un processo morto, non uno bloccato; per catturare un runner che ha smesso di eseguire il polling, avvisa sulla serie `last_poll_age_seconds` da [`/metrics`](https://code.claude.com/docs/it/self-hosted-environments-reference#prometheus-metrics). Il Deployment di seguito monta il segreto dell'ambiente da un Kubernetes Secret, punta i probe di liveness e readiness a `/healthz`, e imposta un periodo di grazia di terminazione di 90 secondi. Vedi [Shutdown timing](#shutdown-timing) per il motivo per cui il periodo di grazia è importante.

Il manifesto non imposta `resources` di CPU o memoria sul container runner. Aggiungi un blocco dimensionato per la capacità che esegui, come [Dimensiona CPU e memoria per le sessioni](#size-cpu-and-memory-for-sessions) descrive.

```yaml theme={null}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: claude-runner
  namespace: claude-runners
spec:
  replicas: 3
  selector:
    matchLabels:
      app: claude-runner
  template:
    metadata:
      labels:
        app: claude-runner
        app.kubernetes.io/part-of: claude-code-self-hosted-runner
    spec:
      terminationGracePeriodSeconds: 90
      containers:
        - name: runner
          image: <your-registry>/claude-runner:latest
          args:
            - self-hosted-runner
            - --environment-secret-file
            - /etc/claude/environment-secret
            - --capacity
            - "4"
            - --base-dir
            - /workspace
          volumeMounts:
            - name: environment-secret
              mountPath: /etc/claude
              readOnly: true
          ports:
            - name: health
              containerPort: 8080
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 30
      volumes:
        - name: environment-secret
          secret:
            secretName: claude-runner-environment-secret
```

Il Deployment di sopra vive in uno spazio dei nomi `claude-runners`. Crea prima lo spazio dei nomi:

```bash theme={null}
kubectl create namespace claude-runners
```

Crea il Secret di supporto da un file locale che contiene il valore che hai copiato nel passaggio [**Copy environment key**](https://code.claude.com/docs/it/self-hosted-environments-quickstart.md#set-up-manually) dell'interfaccia utente di amministrazione, in modo che il segreto non appaia mai nella tua cronologia della shell. Esegui `(umask 077 && cat > ./environment-secret)`, incolla il segreto, premi Invio, quindi Ctrl-D. Quindi crea il Secret e cancella il file:

```bash theme={null}
kubectl create secret generic claude-runner-environment-secret -n claude-runners --from-file=environment-secret=./environment-secret
```

## Docker Compose

Il servizio Compose di seguito riavvia il runner ogni volta che esce, il che copre sia i crash che l'uscita normale dopo il drenaggio. Una politica di riavvio di Docker riavvia lo stesso container con il suo strato scrivibile intatto, quindi il runner torna su un filesystem riutilizzato piuttosto che su uno fresco che la [postura hardening](#harden-your-deployment) consiglia; usa questa ricetta per la valutazione, e per la produzione ricrea il container per esecuzione o usa un orchestrator che lo fa.

Docker attende più a lungo prima di ogni riavvio di un container che continua a uscire, fino a un limite massimo, quindi un runner che non riesce ad avviarsi non continua a riavviarsi in un ciclo stretto secondo questa ricetta. [Quando il runner esce](#when-the-runner-exits) descrive cosa controllare quando ciò accade.

```yaml theme={null}
services:
  claude-runner:
    image: <your-registry>/claude-runner:latest
    command:
      - self-hosted-runner
      - --environment-secret-file
      - /run/secrets/environment-secret
      - --capacity
      - "4"
      - --base-dir
      - /workspace
    secrets:
      - environment-secret
    restart: always
    stop_grace_period: 90s

secrets:
  environment-secret:
    file: ./environment-secret
```

## Shutdown timing

Dopo `SIGTERM`, un runner ha bisogno di tempo per chiudere le sue sessioni in modo pulito prima che il tuo orchestrator lo termini. All'avvio registra nei log quanto tempo gli serve, in una riga che contiene `This runner needs up to 80s` con le impostazioni predefinite. Imposta il timeout di arresto del tuo orchestrator ad almeno quel numero di secondi: `terminationGracePeriodSeconds` su Kubernetes, `stop_grace_period` su Docker Compose, o l'equivalente della tua piattaforma. Il valore predefinito di Kubernetes è 30 secondi, quindi senza questa impostazione può fermare il pod prima che il runner finisca.

Su `SIGTERM`, il runner smette di accettare nuove sessioni. Poi avvia un arresto controllato, chiamato drenaggio, a meno che tu non imposti [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal) per ritardarlo. Il drenaggio ha tre passaggi:

1. Il runner aspetta fino a [`--drain-wait-sec`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) secondi, `0` per impostazione predefinita, affinché i turni ancora in esecuzione finiscano.
2. Termina il process tree di ogni sessione, incluso qualsiasi comando che Claude stava ancora eseguendo, ma non [un processo ancora in esecuzione dopo che il suo comando shell è terminato](#processes-a-stopped-session-leaves).
3. Esegue il [`post-session` lifecycle hook](https://code.claude.com/docs/it/self-hosted-environments-configuration#post-session).

Il runner continua a eseguire il polling verso Anthropic per tutta la durata del drenaggio. Questo mantiene le sue sessioni assegnate a esso, così un altro runner non ne prende una mentre l'hook `post-session` sta ancora salvando il lavoro non committato.

Il tempo registrato nei log è la somma di questi valori:

* `--drain-wait-sec` per il passaggio 1, 0 secondi per impostazione predefinita
* [`--session-stop-grace-sec`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) per il passaggio 2, 5 secondi per impostazione predefinita
* [`--post-session-hook-timeout-sec`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) per il passaggio 3, 60 secondi per impostazione predefinita
* Un margine fisso di 15 secondi
* 30 secondi in più quando [`--push-outcome-on-release`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) è impostato

Con le impostazioni predefinite il totale è 0 + 5 + 60 + 15 = 80 secondi. Un valore più alto di `--capacity` non lo aumenta, perché il runner drena tutte le sue sessioni contemporaneamente.

Concedi più tempo se imposti uno di questi flag:

* **Con [`--retire-at`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags)**: lascia abbastanza tempo tra il momento del ritiro e il momento in cui l'host si ferma perché i turni tipici finiscano, più l'attesa per le attività in background descritta in [Runner lifecycle](https://code.claude.com/docs/it/self-hosted-environments#runner-lifecycle), più il tempo registrato nei log. Calcola il momento del ritiro a ogni avvio, ad esempio `date +%s` più la durata prevista del runner.
* **Con [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal)**: il timeout di arresto deve coprire anche i minuti che configuri e un'ulteriore attesa prima che il drenaggio inizi, 75 secondi con le impostazioni predefinite. [Defer the drain past the first signal](#defer-the-drain-past-the-first-signal) spiega entrambi. Il runner registra nei log anche questo tempo più lungo all'avvio.

### Lascia finire i turni in esecuzione prima di un riavvio

Per lasciare che i turni in esecuzione finiscano prima di un riavvio rolling, imposta `--drain-wait-sec` e aumenta il timeout di arresto ad almeno quell'attesa più 80 secondi. Con l'attesa predefinita di `0`, un riavvio interrompe qualsiasi turno ancora in esecuzione, e la sessione riprende su un altro runner senza il suo [lavoro non pushato](#additional-limitations).

* **`--drain-wait-sec`**: all'incirca la durata dei turni e delle attività in background che vuoi lasciar finire. È un massimo: l'attesa termina prima se finiscono prima.
* **Il tuo timeout di arresto**: `terminationGracePeriodSeconds` in Kubernetes, `stop_grace_period` in Docker Compose. Ad esempio, con `--drain-wait-sec 120`, sostituisci il `90` nella ricetta [Kubernetes](#kubernetes) o [Docker Compose](#docker-compose) con `200`.
* **Se hai modificato altre impostazioni di arresto**: gli 80 secondi valgono per le impostazioni predefinite. Avvia un runner con i tuoi flag e usa almeno il numero nella sua riga di avvio `This runner needs up to`, che include l'attesa. Con [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal) impostato, usa il numero più grande nella riga di avvio successiva, dopo `Size the stop timeout to at least`. [Shutdown timing](#shutdown-timing) elenca cosa si aggiunge a questi numeri.

> [!WARNING]
>
> Se il timeout di arresto scade durante l'attesa, il tuo host termina il runner con lavoro ancora in esecuzione. Le sue sessioni non ottengono nessun hook `post-session`. Inoltre il runner non si deregistra, quindi il piano di controllo può impiegare alcuni minuti, e talvolta di più, per rimetterle in coda.

### Pubblica un messaggio di stato prima di un riavvio

Prima di un riavvio che interromperà le sessioni, chiedi a un [Owner](https://code.claude.com/docs/it/cloud-environments.md#organization-shared-environments) di pubblicare un messaggio di stato sull'ambiente dalla [pagina di amministrazione **Cloud environments**](https://claude.ai/admin-settings/cloud-environments). Su [claude.ai/code](https://claude.ai/code), i membri vedono il messaggio nelle loro sessioni in quell'ambiente. Chiedi all'Owner di impostare una scadenza al momento della pubblicazione, oppure di rimuovere il messaggio una volta che i runner sono di nuovo attivi.

### Defer the drain past the first signal

Imposta [`--defer-shutdown-max-min <n>`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) se desideri che un runner che stai riavviando continui a servire le sessioni che tiene per fino a `n` minuti, invece di drenarle al primo segnale. Al primo `SIGTERM` o `SIGINT`, il runner smette di accettare nuovo lavoro e continua a servire le sessioni che tiene. Continua a eseguire il polling in modo che il piano di controllo non rimetta in coda quelle sessioni. Richiede Claude Code v2.1.238 o successivo.

#### Cosa succede alle sessioni che il runner tiene dopo il primo segnale

Nei primi due stadi che seguono il segnale, il runner rilascia le sessioni, e una sessione rilasciata riprende su un runner nuovo quando il suo utente invia il messaggio successivo. Contando dal primo segnale, il runner attraversa tre stadi:

* **Per i primi `n` minuti**: il runner serve le sue sessioni normalmente e continua ad applicare `--startup-timeout-min` e `--kill-session-after-min`. Se imposti anche [`--release-idle-session-min`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags), il runner rilascia qualsiasi sessione il cui utente è stato inattivo per quel tempo; senza di esso, le sessioni inattive rimangono sul runner.
* **Quando gli `n` minuti scadono**: il runner rilascia ogni sessione che ancora tiene, inattiva o no. Il runner aspetta che il turno di una sessione a metà turno finisca, e fino a 60 secondi in più per le attività in background di un turno, prima di rilasciare quella sessione.
* **Quando la grazia post-release scade**: il runner drena qualsiasi sessione che ancora tiene, e il piano di controllo rimette subito in coda ogni sessione drenata a un altro runner. La grazia post-release inizia quando gli `n` minuti scadono ed è di 75 secondi con i valori predefiniti. Se imposti `--drain-wait-sec` sopra i 60 secondi, la grazia post-release è invece `--drain-wait-sec` più 15 secondi.

In qualsiasi stadio, il runner esce con 0 non appena non tiene più sessioni. Un secondo segnale interrompe gli stadi: il runner drena immediatamente, come fa al primo segnale senza `--defer-shutdown-max-min`. Una volta che un drenaggio è in corso, il segnale successivo forza l'uscita del runner. Questo vale sia che il drenaggio sia stato avviato da un secondo segnale sia dalla scadenza della grazia post-release.

#### Dimensiona il timeout di arresto

Dai al timeout di arresto del tuo host almeno la somma di tre parti: gli `n` minuti che configuri, la grazia post-release, e il drenaggio che [Shutdown timing](#shutdown-timing) descrive. Con le impostazioni predefinite la grazia post-release è di 75 secondi e il drenaggio richiede fino a 80 secondi, quindi concedi `n` minuti più 155 secondi. Il runner stampa questa somma all'avvio ogni volta che `--defer-shutdown-max-min` è impostato.

Se il timeout di arresto scade prima che il runner finisca, le sessioni che ancora tiene non ottengono nessun hook `post-session`. Il runner non si deregistra, quindi il piano di controllo può impiegare alcuni minuti, e talvolta di più, per rimettere in coda quelle sessioni. Se non puoi dare al timeout di arresto quella somma, lascia `--defer-shutdown-max-min` non impostato in modo che il runner dreni invece al primo segnale.

### Cosa raggiunge un hook post-session in esecuzione

L'hook `post-session` e il processo figlio della sessione Claude vengono eseguiti ciascuno nel proprio gruppo di processi POSIX, separato da quello del runner, quindi i meccanismi di arresto li raggiungono in modo diverso:

* **Un `SIGTERM` mentre il runner sta già drenando**: forza immediatamente l'uscita del runner, saltando tutto ciò che rimane del drenaggio. Senza [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal), questo è il secondo `SIGTERM` che il runner riceve. Nulla invia segnali a un hook `post-session` in piena esecuzione, quindi su un host nudo dove un processo init adotta gli orfani, finisce da solo, ma senza supervisione: il suo budget di timeout non si applica più, e una scrittura sulla pipe di log chiusa può terminarlo con `SIGPIPE`, quindi un hook che deve sopravvivere a un'uscita forzata in quel contesto dovrebbe reindirizzare il proprio output a un file. Nelle ricette per container di questa pagina il runner è il PID 1 del container e la sua uscita termina il container, e con il `KillMode=control-group` predefinito di systemd la terminazione a livello di cgroup raggiunge anche l'hook, come descrive la voce **Cgroup-wide kills**; in entrambi i casi, considera un'uscita forzata come fatale per l'hook e affidati invece al periodo di grazia.
* **Segnali a livello di gruppo di processi**, come `kill -- -<pid>` in uno script wrapper, il job control della shell, o un watchdog a livello di gruppo: raggiungono il runner e un sottoprocesso di un hook `checkout` in esecuzione, che rimane deliberatamente collegato al gruppo, ma non un hook `post-session` in piena esecuzione né il processo figlio della sessione.
* **Cgroup-wide kills**, come il `KillMode=control-group` predefinito di systemd o il `SIGKILL` che Kubernetes invia all'intero container quando `terminationGracePeriodSeconds` scade: raggiungono tutto, incluso l'hook. L'isolamento del gruppo di processi non protegge da questi, motivo per cui il periodo di grazia deve coprire l'intero drenaggio.
* **Il timeout dell'hook stesso**: quando un hook supera `--post-session-hook-timeout-sec`, il runner invia `SIGTERM` all'intero gruppo di processi dell'hook, quindi `SIGKILL` due secondi dopo, in modo che un worker generato dall'hook con fork, come tar, rsync o git, termini insieme alla shell wrapper invece di sopravvivere come orfano. La supervisione del runner termina una volta che lo stdio dell'hook si chiude: un worker che ha reindirizzato il proprio output a un file e sopravvive allo stadio `SIGTERM` è fuori dalla portata del runner.

Quando il drenaggio inizia, e di nuovo in caso di uscita forzata, il runner registra nei log quanti hook `post-session` sono ancora in esecuzione, in modo che tu possa distinguere un drenaggio tranquillo da uno che è a metà di uno snapshot.

## Mantieni la directory di base e la capacità identiche tra i runner

Se un runner muore a metà sessione, il server rimette in coda la sessione e un altro runner nell'ambiente la raccoglie. Quel runner deriva il percorso di checkout dal suo proprio `--base-dir` e `--capacity`: `--capacity 1` controlla direttamente sotto `--base-dir`, e un `--capacity` sopra `1` usa worktree per sessione invece. Quando i runner nello stesso ambiente usano valori diversi per uno dei due flag, la directory di lavoro della sessione ripresa cambia, e i percorsi assoluti che l'agente ha registrato in precedenza, in modifiche, chiamate di strumenti, o le sue stesse note, puntano a una posizione che non esiste più.

Usa lo stesso `--base-dir` e `--capacity` su ogni runner in un ambiente, e non usare un valore per host come un ID istanza o nome host. Passa entrambi i flag esplicitamente, come fanno le ricette [Kubernetes](#kubernetes) e [Docker Compose](#docker-compose), in modo che nessuno dei due valori dipenda da un valore predefinito.

La directory di base è predefinita a `/workspace`, con l'eccezione che la riga di riferimento [`--base-dir`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) registra. Il runner ha bisogno di accesso in scrittura ad essa. All'avvio, prima di registrarsi, il runner crea la directory e conferma che può scrivere ad essa, e esce con `cannot create or write to base directory` quando non può. Un runner avviato come root crea il `/workspace` predefinito da solo. Per un runner non root, crea la directory e dai al runner la proprietà dell'utente prima di avviare il runner, o punta `--base-dir` a una directory che l'utente già possiede.

## Riutilizza un checkout pre-riscaldato

Per i repository grandi, il clone può dominare l'avvio della sessione. Per saltare il clone freddo, fornisci tu stesso un clone al percorso in cui il runner mantiene il proprio. Senza un [`checkout` hook](https://code.claude.com/docs/it/self-hosted-environments-configuration#checkout), il runner mantiene un clone canonico per repository a `<base-dir>/<repo-owner>/<repo>` e lo riutilizza tra le sessioni:

* **A `--capacity 1`**: il runner recupera il ref richiesto, stacca `HEAD` e lo resetta duramente, il che è quasi istantaneo quando poco è cambiato.
* **A una `--capacity` superiore a uno**: il runner esegue il fetch in quel clone, quindi estrae da esso un worktree separato per ogni sessione. Un clone pre-riscaldato fa risparmiare il download ma non il checkout.

Fornisci il clone nell'immagine o su un volume persistente:

* **Clone nell'immagine**: costruisci il clone nella tua immagine runner a quel percorso. Ogni container fresco inizia quindi con il clone caldo senza riutilizzare un disco.
* **Clone su un volume persistente**: su runner che pre-blocchi a un account di un utente con [`--lock-to-account`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags), punta `--base-dir` a un volume persistente, in modo che il disco serva solo quell'account. Un runner pre-bloccato non raccoglie mai sessioni di canale Claude Tag, quindi questa opzione non si applica ai runner che le servono.

Cosa il percorso di riutilizzo fa e non garantisce:

* **Profondità del clone**: crea il clone senza `--depth`, con o senza `--single-branch`. Il runner non passa mai `--depth` quando esegue il fetch in un clone con un remote `origin`, anche se imposti [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/it/self-hosted-environments-reference#environment-variable-only-settings). Con un clone completo, ogni sessione scarica solo i commit che mancano al clone. Con un clone shallow, una sessione su un branch che si è separato prima del commit più vecchio del clone scarica l'intera cronologia di quel branch.
* **Le modifiche tracciate si resettano, i file non tracciati persistono**: a `--capacity 1`, ogni sessione inizia da un reset duro che cancella le modifiche tracciate della sessione precedente, ma il runner non esegue mai `git clean`, quindi i file non tracciati dalle sessioni precedenti dell'owner bloccato rimangono nell'albero.
* **Directory per sessione persistono anche**: accanto al checkout, il runner crea voci per sessione sotto `<base-dir>/_sessions/` per ogni sessione che esegue. La directory di configurazione Claude della sessione contiene una copia locale della trascrizione della conversazione. Accanto ad essa si trovano i file caricati della sessione, quando la sessione ne ha. La directory della sessione si trova lì anche: contiene qualsiasi worktree per sessione e checkout di hook `checkout` mentre la sessione è in esecuzione, e mantiene qualsiasi altra cosa Claude abbia scritto in essa.

  Per impostazione predefinita il runner lascia questi in posizione quando la sessione termina, quindi su un disco che sopravvive al processo runner si accumulano. Ogni sessione viene eseguita come l'utente del runner stesso, quindi qualsiasi sessione successiva che il disco serve può leggerli. Se mantieni un `--base-dir` persistente, dimensiona il volume per quella crescita. Lo stesso si applica a qualsiasi configurazione che riavvia il runner sullo stesso filesystem, inclusa la [ricetta Docker Compose](#docker-compose).
* **Con `--remove-session-state`, le directory per sessione non persistono**: avvia il runner con [`--remove-session-state`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) per fargli eliminare le directory per sessione di ogni sessione mentre la sessione termina. L'eliminazione è best-effort: le directory rimangono quando il runner viene ucciso prima che la sua pulizia venga eseguita. Il clone canonico e i file che una sessione ha scritto altrove sull'host, come la directory temporanea, rimangono comunque.
* **Con il proxy git, il reset diventa un checkout**: con [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), il runner sanitizza il `.git/` del clone prima di ogni sessione. Mantiene l'object store e lo stato shallow, ed elimina l'indice. Senza l'indice, ogni sessione inizia con un checkout completo dell'albero di lavoro invece di un reset quasi istantaneo. Il runner esegue comunque il fetch nel clone esistente invece di clonare di nuovo il repository. I pre-warm dei submodule non sono supportati sotto il proxy.
* **Con il proxy git, un pre-warm `reftable` perde i suoi ref**: quando il runner sanitizza `.git/` prima di ogni sessione, mantiene `.git/refs/` e rimuove la directory `.git/reftable/`. Un clone pre-riscaldato nel formato dei ref `reftable` di git perde così i suoi ref, quindi pre-riscalda invece il clone nel formato dei ref `files` di git.
* **I clone lunghi non hanno bisogno di workaround**: il runner limita ogni operazione git con un watchdog senza progresso di 120 secondi e un hard cap di 30 minuti, non un timeout piatto, quindi un clone freddo lento che continua a segnalare progresso si completa.

### Conferma che il runner usa il tuo clone

Per confermare che il runner usa il tuo clone, avvialo con [`--log-level debug`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) e avvia una sessione sul repository. Il runner registra `Prefetched repo found at` e il percorso del clone. Se invece registra `No prefetched repo, fresh init at`, non ha trovato alcun clone utilizzabile al percorso indicato in quella riga. Verifica che il tuo clone si trovi in quel percorso, sia di proprietà dell'utente del runner e abbia un remote chiamato `origin`.

Se il runner trova il tuo clone e le sessioni si avviano comunque lentamente, verifica se il clone è shallow.

## Fissa la versione

Per impostazione predefinita, il processo figlio di ogni sessione esegue il binario del runner stesso, e il runner disattiva l'auto-aggiornamento all'interno delle sessioni che genera. Con questa impostazione predefinita, ogni sessione esegue la versione che hai installato sull'host o incluso nell'immagine. Un aggiornamento a livello di host ha effetto la prossima volta che il runner si avvia.

Scegli quale versione eseguono le tue sessioni e quando cambia:

* **Prima di fissare una versione**: controlla [le versioni di Claude Code che i modelli richiedono](https://code.claude.com/docs/it/model-config.md#available-models) per ogni modello che le tue sessioni utilizzano. Se un modello richiede una versione più recente di quella eseguita dalle tue sessioni, il server rifiuta le richieste per quel modello con [Claude Code does not support this model](https://code.claude.com/docs/it/errors.md#claude-code-does-not-support-this-model).
* **Per mantenere una flotta su una versione**: costruisci l'immagine con una versione fissata, o su un host nudo installa una versione specifica e [disabilita gli auto-aggiornamenti](https://code.claude.com/docs/it/setup.md#disable-auto-updates)
* **Per aggiornare una flotta fissa**: leggi le voci del [changelog](https://code.claude.com/docs/en/changelog.md) tra la tua versione e quella che stai installando, quindi installa la versione più recente o ricompila l'immagine e riavvia i runner
* **Per aggiornare i runner on-demand**: leggi le voci del [changelog](https://code.claude.com/docs/en/changelog.md) tra la tua versione e quella che stai installando, quindi cambia l'immagine che il tuo [hook `spawn-runner`](https://code.claude.com/docs/it/self-hosted-environments-configuration#the-spawn-runner-hook) avvia. Ogni nuovo runner ottiene la nuova versione. Un runner già attivo, incluso un runner in standby avviato da [`--min-idle`](https://code.claude.com/docs/it/self-hosted-environments-reference#orchestrator-cli-flags), mantiene la sua versione finché non termina. Non riavviarlo, perché il suo ordine di lavoro è monouso.
* **Se le sessioni avviano un binario diverso**: se usi [`--exec-path`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) o un [hook `command`](https://code.claude.com/docs/it/self-hosted-environments-configuration#command) per avviare un binario di Claude Code diverso da quello del runner, le sessioni eseguono la versione di quel binario. Aggiorna quel binario quando aggiorni il runner.
* **Plugin**: i marketplace dei plugin non si auto-aggiornano neanche; imposta `FORCE_AUTOUPDATE_PLUGINS=1` nell'ambiente del runner per lasciare che i plugin si auto-aggiornino mentre il binario rimane fissato

## Scala la flotta

Il tuo orchestrator decide quando aggiungere o rimuovere runner. A causa del [blocco one-owner-per-runner](https://code.claude.com/docs/it/self-hosted-environments#runner-lifecycle), il numero minimo di repliche è il numero di utenti e agenti Claude Tag che ti aspetti siano attivi contemporaneamente; `--capacity` controlla il parallelismo all'interno delle sessioni di un owner, non tra owner.

Due approcci di scaling sono disponibili:

* **Flotta fissa**: esegui un set statico di repliche runner e scala sulle [metriche Prometheus](https://code.claude.com/docs/it/self-hosted-environments-reference.md#prometheus-metrics) che ogni runner serve
* **Runner on-demand**: esegui il sottocomando `claude self-hosted-runner orchestrator`, che esegue il polling di Anthropic per le sessioni in coda senza runner disponibile e invoca il tuo hook `spawn-runner` per avviarne uno per sessione. Vedi [Runner on-demand](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#on-demand-runners).

### Rilascia le sessioni inattive su una flotta fissa

Se le sessioni restano in coda su una flotta fissa mentre tutti i runner sono online, la causa potrebbe essere costituita dalle sessioni inattive. Per impostazione predefinita, una sessione mantiene il suo slot dopo la fine del suo turno, e il suo runner resta bloccato sull'[owner](https://code.claude.com/docs/it/self-hosted-environments#key-concepts) di quella sessione. Se l'[attività pianificata cloud](https://code.claude.com/docs/it/routines.md) del tuo team avvia una nuova sessione a ogni esecuzione, le sue sessioni inattive possono occupare tutti gli slot.

Per verificarlo, leggi `claude_code_self_hosted_runner_session_idle_seconds` nelle [metriche Prometheus](https://code.claude.com/docs/it/self-hosted-environments-reference.md#prometheus-metrics) di ogni runner. Ogni sessione detenuta da un runner ha la propria serie: `0` mentre la sessione è occupata, altrimenti i secondi trascorsi da quando è diventata inattiva. Scegli il tempo di inattività che vuoi consentire: più lungo delle pause che le persone fanno a metà di un'attività, e abbastanza breve da rilasciare ogni esecuzione prima che inizi quella successiva dell'attività pianificata. Confronta le serie con questo valore.

* **Serie sopra quel tempo, ad esempio `1800` per 30 minuti**: le sessioni inattive occupano quegli slot, quindi imposta `--release-idle-session-min`
* **Tutte le serie sotto quel tempo**: nessuna sessione è rimasta inattiva così a lungo. Aggiungi runner, oppure imposta [`--kill-session-after-min`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) per le [sessioni che non diventano mai inattive](#some-sessions-don’t-count-as-idle).
* **Un runner senza serie**: nessuna sessione lo occupa. Consulta **Sessions stay queued** in [Risoluzione dei problemi](#troubleshooting).
* **Un runner le cui metriche non rispondono**: il suo processo potrebbe essere terminato. Consulta **An environment that looks healthy** in [Quando il runner termina](#when-the-runner-exits).

Per rilasciare le sessioni inattive, avvia ogni runner con [`--release-idle-session-min`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) e quel tempo in minuti, ad esempio `--release-idle-session-min 30`. Il runner rilascia quindi qualsiasi sessione che resta inattiva così a lungo, sia che l'abbia avviata un'attività pianificata sia una persona.

* **Lavoro non inviato**: una sessione rilasciata riprende al messaggio successivo, senza il lavoro di cui non aveva fatto il push. Per conservarne i commit, consulta [Le sessioni riprese perdono il lavoro non inviato](#additional-limitations).
* **Uscita del runner**: un runner che rilascia la sua ultima sessione avvia una [normale uscita](#when-the-runner-exits). Fai in modo che il tuo supervisor lo riavvii su un filesystem pulito, come descritto in [Rafforza il tuo deploy](#harden-your-deployment).
* **Sessioni che non diventano mai inattive**: imposta anche [`--kill-session-after-min`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags), perché [alcune sessioni non vengono considerate inattive](#some-sessions-don’t-count-as-idle)

## Problemi noti e limitazioni

Le seguenti sono le limitazioni in questa versione, con workaround dove uno esiste.

### Il traffico del connettore lascia la tua rete

Anthropic chiama gli strumenti del connettore dalla sua stessa infrastruttura piuttosto che dal tuo runner. Gli strumenti del connettore sono i connettori claude.ai, come GitHub, Slack, e Linear. Quando Claude usa un connettore in una sessione self-hosted, quel traffico va attraverso `api.anthropic.com` piuttosto che originare all'interno del tuo confine di rete.

Per mantenere un connettore fuori dalle sessioni self-hosted, filtralo con le [impostazioni di politica `allowedMcpServers` e `deniedMcpServers`](https://code.claude.com/docs/it/managed-mcp#policy-based-control-with-allowlists-and-denylists). Claude Code applica queste impostazioni ai connettori che Anthropic consegna così come ai server che semini dall'host runner e ai server che gli utenti aggiungono, quindi se distribuisci una lista di indirizzi consentiti per altri server, Claude Code blocca anche i connettori consegnati. Per mantenere i connettori disponibili insieme a una lista di indirizzi consentiti basata su URL, aggiungi voci che corrispondono ai percorsi proxy di Anthropic per i connettori consegnati:

* `https://api.anthropic.com/v2/ccr-sessions/*`
* `https://api.anthropic.com/v1/code/sessions/*`
* `https://api.anthropic.com/v1/code/mcp/*`

Se il traffico dello strumento deve rimanere all'interno della tua rete, esegui gli strumenti equivalenti come server MCP locali sull'immagine del runner invece. Vedi [Server MCP](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#mcp-servers).

### Alcune sessioni non contano come inattive

Una sessione che tiene un compito di background che non finisce mai non conta come inattiva, quindi `--release-idle-session-min` non rilascerà lo slot di quella sessione. Una sessione che sta aspettando un'approvazione richiesta dall'interno di una chiamata di strumento in esecuzione non conta neanche come inattiva. Imposta sempre `--kill-session-after-min` insieme ad essa come un hard backstop in modo che nessuna sessione possa tenere uno slot indefinitamente.

`--kill-session-after-min` è un backstop per le sessioni runaway. Su un runner su v2.1.260 o successivo, una sessione che raggiunge il limite non viene terminata immediatamente. Il runner le dà una finestra di grazia, 15 minuti per impostazione predefinita, che puoi cambiare con [`SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS`](https://code.claude.com/docs/it/self-hosted-environments-reference#environment-variable-only-settings):

* Se la sessione sta aspettando il suo utente, il runner la rilascia. Se il suo turno è finito e tiene solo compiti di background, il runner aspetta fino a 60 secondi affinché questi compiti finiscano e poi la rilascia. La sessione riprende quando il suo utente invia il suo prossimo messaggio.
* Se un turno è ancora in esecuzione, il runner aspetta che il turno finisca, o che la sessione aspetti successivamente il suo utente, e poi la rilascia.
* Se la sessione è ancora sul runner quando la finestra di grazia scade, il runner la termina, e il lavoro di qualsiasi turno in esecuzione è perso. Un turno che aspetta un'approvazione richiesta dall'interno di una chiamata di strumento in esecuzione è un modo in cui una sessione sopravvive alla finestra.

Una sessione rilasciata riprende da un clone fresco, quindi il lavoro che non aveva spinto è comunque perso; vedi [Le sessioni riprese perdono il lavoro non spinto](#additional-limitations). Prima di v2.1.260, il runner terminava ogni sessione al limite, dopo aver aspettato al massimo la finestra di grazia affinché un turno in esecuzione finisca.

Imposta il flag sopra la tua sessione più lunga prevista, come `--kill-session-after-min 480` per 8 ore. Per liberare slot dalle conversazioni che diventano inattive, usa `--release-idle-session-min` invece.

### Limitazioni aggiuntive

* **Le sessioni riprese perdono il lavoro non spinto**: un nuovo runner clona di nuovo il repository dal suo branch iniziale, quindi il lavoro che la sessione non aveva spinto è perso.
  * **Per conservare il lavoro di cui è stato fatto il commit**: imposta [`--push-outcome-on-release`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags) su ogni runner nell'ambiente, perché un runner senza il flag riprende la sessione dal suo branch iniziale. Un runner con il flag esegue un push best-effort dei branch di risultato della sessione prima del rilascio, e la sessione ripresa parte da quei commit. Il push usa le credenziali git proprie dell'host del runner, anche su un runner che usa [git gestito da Anthropic](#use-the-anthropic-git-proxy).
  * **Con un hook `checkout`**: i repository estratti tramite un [hook del ciclo di vita `checkout`](https://code.claude.com/docs/it/self-hosted-environments-configuration#checkout) non vengono sottoposti a push. Crea invece uno snapshot di questi repository dall'[hook `post-session`](https://code.claude.com/docs/it/self-hosted-environments-configuration#post-session).
  * **Prima di abilitare il flag**: limita chi può eseguire il push sui ref `claude/*` sul remote di origine. Alla ripresa, il runner recupera il branch precedentemente spinto senza verificare chi ne ha eseguito il push.
  * **Le modifiche senza commit vengono comunque perse**: per chiedere a Claude di fare il commit e il push quando finisce di rispondere, vedi [Chiedere alle sessioni di eseguire il push del proprio lavoro](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#prompt-sessions-to-push-their-work)
* **Un comando interrotto dalla perdita di un runner può essere eseguito di nuovo**: quando la sessione riprende su un altro runner, Claude vede la chiamata allo strumento interrotta con un risultato che indica che il suo esito è sconosciuto.
* **Un repository aggiunto a metà sessione può non riuscire a essere clonato**: Claude lo clona con `git clone` tramite HTTPS. Su un runner senza [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), il clone fallisce con un errore di autenticazione git se nulla sull'host può leggere il repository. Quando possibile, seleziona ogni repository di cui la sessione ha bisogno quando la crei.
* **Alcuni connettori non appaiono nelle sessioni self-hosted**: un connettore che non hai ancora connesso nelle Impostazioni di claude.ai non è elencato in una sessione self-hosted, e la sessione non ti chiederà di connettarlo. Connettilo prima nelle Impostazioni, quindi avvia una sessione fresca. L'aggiunta di un connettore a una sessione già in esecuzione non rende i suoi strumenti disponibili a Claude; avvia una sessione fresca per raccogliere un connettore appena aggiunto.
* **Quando qualcuno archivia o elimina una sessione, o rilascia il suo runner dal menu della sessione**: non fare affidamento sul push di [`--push-outcome-on-release`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags). Per salvare i commit in questi casi, esegui il push di `HEAD` su un branch di recupero da un [hook `post-session`](https://code.claude.com/docs/it/self-hosted-environments-configuration#post-session). Se parti dall'esempio presente lì, fai in modo che esegua il push anche quando non ci sono modifiche senza commit, perché esegue il push solo dopo un commit di snapshot.

### Segnala un problema

Per i problemi con gli ambienti self-hosted, contatta il tuo team di account Anthropic.

## Troubleshooting

Per una diagnosi guidata, eseguire il subcommand doctor sull'host del runner. Il subcommand doctor avvia una sessione Claude Code interattiva con i log e lo stato del runner allegati. Accedere con `claude auth login` su quell'host prima in modo che la sessione possa interrogare l'ambiente, i suoi runner e le sue sessioni in coda. Senza questo accesso, ad esempio quando l'host si autentica con una chiave API, è limitato all'endpoint di salute locale, alle metriche e al log del runner, e legge il log solo se è stato avviato il runner con `--log-file`.

```bash theme={null}
claude self-hosted-runner doctor
```

Problemi comuni:

* **Il runner non appare nell'ambiente**: confermare che l'host possa raggiungere `api.anthropic.com` su HTTPS, che il segreto dell'ambiente sia attuale e che l'orologio dell'host sia entro cinque minuti dall'ora reale; uno scostamento maggiore causa il fallimento dell'autenticazione. Il runner registra `[runner:fatal]` con il motivo del rifiuto in caso di errore di autenticazione.
* **Il runner esce all'avvio con `cannot create or write to base directory`**: il runner non può creare o scrivere in `--base-dir`, che per impostazione predefinita è `/workspace`. Correggere la proprietà della directory o puntare `--base-dir` a un percorso scrivibile, come descritto in [Keep the base directory and capacity identical across runners](#keep-the-base-directory-and-capacity-identical-across-runners). Se il runner registra invece `[runner:fatal]` dicendo che il controllo della directory di base è scaduto, la directory si trova su un mount NFS o CSI bloccato. Controllare l'integrità del mount piuttosto che i permessi. Il runner stampa entrambi questi errori di avvio su stderr prima di aprire `--log-file`, quindi cercarli nel terminale o nei log del container della piattaforma piuttosto che nel file di log. Prima della v2.1.225, il runner non controllava la directory di base all'avvio e questa configurazione errata causava il fallimento delle sessioni dopo il pickup.
* **Il runner esce all'avvio con `RegisterRunner failed:` e un errore di certificato TLS**: l'errore può essere, ad esempio, `self signed certificate in certificate chain`. Il runner non considera attendibile l'autorità di certificazione (CA) dietro il certificato che ha ricevuto, come accade dietro un proxy che ispeziona il traffico TLS. Imposta [`NODE_EXTRA_CA_CERTS`](https://code.claude.com/docs/it/network-config#custom-ca-certificates) nell'ambiente del runner su un file PEM con la CA radice del proxy.

  Non devi impostarla di nuovo per le sessioni, che la ereditano. Git non la legge: per i clone attraverso quel proxy, imposta anche [`GIT_SSL_CAINFO`](#trust-a-private-certificate-authority-with-anthropic-managed-git).
* **Il tuo script di avvio stampa un errore di scrittura contenente `Resource temporarily unavailable` ed esce**: il tuo script condivide il proprio stdout o stderr con il processo `claude self-hosted-runner` o [orchestrator](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#on-demand-runners) che avvia, il quale ha impostato quella pipe o quel socket condiviso in modalità non bloccante. Quando la pipe è piena, una scrittura dal tuo script fallisce con questo errore del sistema operativo invece di attendere, e con `set -e` lo script esce.

  Per eliminare l'errore, invia l'output del runner o dell'orchestrator a un file che l'utente del tuo script può creare. Aggiungi `>> "$HOME/claude-runner.log" 2>&1` al comando che lo avvia, prima di qualsiasi `&` che lo mette in background:

  ```bash theme={null}
  claude self-hosted-runner --environment-secret-file /etc/claude/environment-secret >> "$HOME/claude-runner.log" 2>&1
  ```

  L'output del tuo script continua ad andare dove andava prima, mentre ciò che il runner scrive su stdout e stderr, inclusi i suoi errori di avvio, va in quel file. Usa questo reindirizzamento della shell anziché il solo [`--log-file`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags), che replica il log in un file mentre il runner continua a scrivere su stdout e stderr. Se passi già `--log-file`, mantienilo comunque, perché il reindirizzamento non cambia ciò che quel file riceve.
* **L'orchestrator registra `poll failed:` con un errore di certificato TLS e continua a riprovare**: l'orchestrator non considera attendibile la CA dietro il certificato che ha ricevuto, come accade dietro un proxy che ispeziona il traffico TLS. Imposta [`NODE_EXTRA_CA_CERTS`](https://code.claude.com/docs/it/network-config#custom-ca-certificates) nell'ambiente dell'orchestrator su un file PEM con la CA radice del proxy. Impostala anche per i runner avviati dal tuo hook `spawn-runner`.

  Ad esempio, una riga può terminare con `poll failed: self signed certificate in certificate chain — retrying in 1s`, con le parole della libreria TLS tra i due punti e il trattino. Per verificare la correzione, controlla che `connected` sia `true` nel corpo di [`/healthz`](https://code.claude.com/docs/it/self-hosted-environments-reference#health-endpoint) dell'orchestrator.
* **Le sessioni rimangono in coda**: ogni runner online può essere bloccato a un proprietario diverso. Controllare la [metrica](https://code.claude.com/docs/it/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_locked_account` di ogni runner o il campo `locked_account` della sua riga di log `[runner:health]` per vedere chi la detiene. Entrambi mostrano l'email del proprietario solo dopo che il runner ha ricevuto un token di sessione con un claim `act.email`, che le sessioni di un agente Claude Tag non hanno mai. Senza il claim, il runner non emette alcuna serie `locked_account` e registra `locked_account=yes`, il che indica che il runner è bloccato ma non a quale proprietario. Aggiungere repliche o attendere che un runner esistente si svuoti e si riavvii. Se l'ambiente utilizza runner on-demand, controllare l'orchestrator; vedere [On-demand runners](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#on-demand-runners).
* **Le sessioni falliscono subito dopo la presa in carico**: apri la sessione in claude.ai/code per vedere l'errore. Se l'errore mostra che git non è riuscito ad autenticarsi, controlla le [credenziali git](#configure-git) del runner. Su un runner avviato con `--use-anthropic-git-proxy`, consulta [Quando le sessioni non si avviano su un runner con il git proxy](#when-anthropic-doesnt-serve-a-session).

  Una directory di base non scrivibile arresta il runner all'avvio invece di far fallire le sessioni. Consulta la voce **Il runner esce all'avvio con `cannot create or write to base directory`** in questo elenco.
* **Una sessione viene presa in carico, ma Claude non risponde mai al suo primo messaggio**: se i tuoi runner raggiungono `api.anthropic.com` attraverso un proxy, configuralo in modo che lasci passare la risposta `/worker/events/stream` della sessione man mano che arriva. Aumentare i timeout del runner non serve. Per confermare la causa nel log del runner, consulta [Una sessione viene presa in carico, ma Claude non risponde mai al primo messaggio](#when-a-sessions-first-message-is-never-answered).
* **Le sessioni non si avviano su un runner che ha impostato `--use-anthropic-git-proxy`**: cerca nel log del runner `access denied by the git proxy`, oppure un errore git che nomina un indirizzo `api.anthropic.com` contenente `/git_proxy/`. Per capire se Anthropic ha servito la sessione e correggere la causa, consulta [Quando le sessioni non si avviano su un runner con il git proxy](#when-anthropic-doesnt-serve-a-session).
* **Le sessioni non riescono a raggiungere la rete attraverso un proxy di uscita autenticante**: quando l'origine impostata con [`--proxy-authorization-command` o `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) fallisce, scade dopo 30 secondi o produce un valore vuoto, il runner risponde a quella connessione con `502 Bad Gateway` e registra il motivo. Il runner redige lo stderr del comando in quel log e non registra mai il valore dell'intestazione. Con `--proxy-authorization-command`, eseguire il comando stesso sull'host per confermare che stampa l'intero valore dell'intestazione su stdout. Se il runner esce invece all'avvio con `could not start the proxy-authorization listener`, non ha potuto aprire il suo listener di loopback.
* **Il runner registra righe `Poll failed` contenenti `rejecting the malformed poll response`**: il runner ha ricevuto una risposta di work-poll il cui corpo non è il JSON previsto dalla coda. Una possibile causa è un proxy di intercettazione o un captive portal che risponde con una propria pagina. Se il runner si connette attraverso un proxy, configura il proxy in modo che lasci passare inalterate le risposte da `api.anthropic.com`.

  Il runner rifiuta la risposta malformata, la conteggia sotto il tipo `transport` della [metrica](https://code.claude.com/docs/it/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_poll_errors_total` e riprova secondo la pianificazione per i poll falliti descritta in [Ciclo di vita della sessione](https://code.claude.com/docs/it/self-hosted-environments#session-lifecycle). Il runner continua a servire le sue sessioni attive. Prima della v2.1.246, il runner interpretava una risposta di questo tipo come una coda di lavoro vuota, il che poteva terminare le sue sessioni attive o farlo uscire.
* **Una sessione fallisce con `no longer exists on the remote` o `couldn't find remote ref`**: il runner interpreta il messaggio `couldn't find remote ref` di git come un branch eliminato. Il branch potrebbe comunque esistere. Cercalo sul tuo host git, esattamente con il nome riportato nel messaggio, poi verifica ciascuna causa applicabile:

  * **Il branch non esiste più**: ripristinalo, come chiede il messaggio.
  * **Il branch esiste e non amministri il runner**: invia all'amministratore del runner il messaggio completo e comunicagli che il branch esiste.
  * **Il branch esiste e il runner ha un hook `checkout`**: un comando git nel tuo hook può stampare `couldn't find remote ref` su stderr prima che l'hook esca con un codice diverso da zero. Trova quel comando e correggi il ref che richiede o il remote da cui esegue il fetch. Il runner passa al tuo hook la revisione richiesta dalla sessione come [`CLAUDE_RUNNER_REPO_REF`](https://code.claude.com/docs/it/self-hosted-environments-configuration#checkout).
  * **Il branch esiste e il runner non ha un hook `checkout`**: il runner esegue il fetch del branch da solo. Se al repository si applica un [`--git-host-rewrite`](#rewrite-git-urls-for-private-networks), verifica che l'host verso cui riscrive abbia il branch con lo stesso nome, e in caso contrario esegui il push del branch su quell'host.
  * **Il branch esiste e il runner è precedente alla v2.1.301**: aggiorna il runner come descritto in [Fissa la versione](#pin-the-version), in modo che un errore diverso mostri il proprio messaggio. Prima della v2.1.301, anche altri errori con `couldn't find remote ref` nel loro output venivano segnalati come branch eliminato.

  Una volta aggiornato il runner o corretta la causa, riprova la sessione o avviane una nuova. Se nessuno di questi controlli individua la causa, [segnala un problema](#report-an-issue).

  Per l'origine a cui la sessione invia i risultati, un branch eliminato fa fallire la sessione. Per un'origine git che la sessione legge soltanto, il runner salta quell'origine e continua con le rimanenti. Il runner fa fallire la sessione quando saltare l'origine la lascerebbe senza alcun repository. Prima della v2.1.228, una sessione di questo tipo si avviava in una directory vuota.
* **Claude Tag a volte non pubblica la sua risposta in Slack**: il promemoria di risposta è un [hook fornito dal control plane](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#how-each-session’s-config-is-assembled). Verifica con `claude --version` che il runner sia Claude Code v2.1.229 o successivo, poiché i runner meno recenti ignorano gli hook forniti; verifica che `python3 --version` riesca all'interno dell'immagine del runner, poiché gli hook eseguono `python3`; e verifica che nessun file di impostazioni disattivi gli hook, che si tratti di `disableAllHooks` in `managed-settings.json`, nel `~/.claude/settings.json` dell'utente del runner o nel `.claude/settings.json` o `.claude/settings.local.json` del repository, oppure di `allowManagedHooksOnly` o di uno [`strictPluginOnlyCustomization`](https://code.claude.com/docs/it/settings-reference#strictpluginonlycustomization) che copre gli hook nelle impostazioni gestite.
* **Una sessione inizia senza uno dei suoi repository**: su un runner senza un [hook `checkout`](https://code.claude.com/docs/it/self-hosted-environments-configuration#checkout), l'host git può rifiutare il controllo di accesso del runner per un repository che la sessione legge solo. Il runner quindi salta quel repository, registra una riga `[runner:warn] could not access context source` che nomina il rifiuto, e avvia la sessione su quelli rimanenti.

  Il runner salta solo un rifiuto chiaro: l'host risponde che il repository non è stato trovato, git non trova credenziali per l'host, o l'autenticazione fallisce. Un errore di rete, un timeout, o un HTTP `403` comunque fa fallire l'avvio della sessione, così come un rifiuto per un repository a cui la sessione spinge i risultati. Il runner comunque fa fallire una sessione che saltare lascerebbe senza alcun repository. Con [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), il runner salta solo un repository che il git proxy stesso nega.

  Il controllo di accesso viene eseguito di nuovo ogni volta che la sessione inizia su un runner, quindi una volta che l'identità git del runner ha accesso in lettura, il prossimo avvio clona il repository. Prima della v2.1.274, ognuno di questi rifiuti faceva fallire l'avvio della sessione.
* **Una sessione non si avvia e mostra `This runner couldn't read`**: il runner ha chiesto un repository all'host git con le sue [credenziali git](#configure-git) e ha ricevuto una risposta di repository non trovato. Il repository non esiste, oppure quelle credenziali non possono raggiungerlo.
  * **Se amministri il runner**: concedi alle sue credenziali git l'accesso in lettura al repository, poi avvia una nuova sessione.
  * **Se hai avviato tu la sessione**: se il repository non esiste, avvia una nuova sessione con il repository corretto. Se il runner non riesce a raggiungere il repository, chiedi all'amministratore del runner di concedere l'accesso, poi avvia una nuova sessione.
* **Le sessioni impiegano minuti per avviarsi o riprendere**: una possibile causa è un clone da zero. In una flotta che avvia ogni runner su un disco vuoto, senza [hook `checkout`](https://code.claude.com/docs/it/self-hosted-environments-configuration#checkout), il runner clona da zero i repository della sessione prima di avviare Claude Code. Una sessione che riprende su un disco vuoto attende di nuovo lo stesso clone.

  Per evitare il clone da zero, fornisci un [checkout pre-riscaldato](#reuse-a-pre-warmed-checkout). Se ne fornisci già uno, [verifica che il runner lo usi](#confirm-that-the-runner-uses-your-clone). Dove è il runner stesso a creare il clone ed esce dopo una sola sessione, abbassa [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/it/self-hosted-environments-reference#environment-variable-only-settings) dal valore predefinito di 50 commit per recuperare meno cronologia.

  Se usi un hook, questo viene eseguito al posto del clone del runner, quindi esamina invece ciò che fa il tuo hook. Osserva la [metrica](https://code.claude.com/docs/it/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_session_init_duration_seconds` per vedere quanto durano gli avvii.
* **I turni falliscono con un 401**: quando un turno termina con un 401 o un 403 dall'API di Anthropic, il runner recupera da Anthropic un nuovo [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/it/self-hosted-environments-configuration#wrapper-scripts) e lo passa alla sessione. Il turno fallito non viene ritentato. Questo token ha una durata breve e il runner lo ruota tramite lo stdin della sessione.

  Quando un recupero fallisce, il runner registra una riga `inference_token refresh failed` che dice quando riproverà, e continua a riprovare finché la sessione è in esecuzione.

  Se ogni chiamata inizia a fallire circa 30 minuti in una sessione, uno script wrapper ha probabilmente reciso lo stdin della sessione, quindi le rotazioni dei token non possono raggiungerlo; vedere [Keep stdin and file descriptor 3 attached](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#keep-stdin-and-file-descriptor-3-attached).

  Prima della v2.1.274, il runner smetteva di riprovare un recupero fallito dopo alcuni tentativi e attendeva il prossimo programmato. Un turno fallito non ha attivato un recupero, quindi ogni turno falliva con un 401 fino al prossimo recupero programmato.
* **Un turno fallisce in claude.ai/code con `This model needs a newer version of Claude Code`**: la versione di Claude Code con cui i tuoi runner avviano le sessioni è più vecchia di quella richiesta dal modello della sessione, quindi l'API [rifiuta la richiesta](https://code.claude.com/docs/it/errors.md#claude-code-does-not-support-this-model). Aggiorna i runner alla versione indicata dal messaggio, o a una successiva, come descritto in [Fissa la versione](#pin-the-version). Nel frattempo, gli utenti possono scegliere un modello diverso nella sessione.
* **Il pod viene terminato durante lo scarico**: aumentare `terminationGracePeriodSeconds` ad almeno il valore che il runner registra all'avvio. Vedere [Shutdown timing](#shutdown-timing).

Una volta inizializzato il logging, il runner scrive il suo log del ciclo di vita, incluse le righe `[runner:fatal]`, su stdout e l'output di debug su stderr, il tutto come righe di testo semplice anziché JSON. Gli errori relativi alla directory di base descritti nelle voci di risoluzione dei problemi qui sopra vengono stampati su stderr prima di quel momento. Acquisisci entrambi i flussi con `--log-file`, che consente anche a `self-hosted-runner doctor` di seguirli, oppure con la raccolta dei log della tua piattaforma.

Il processo figlio di ogni sessione scrive un log di debug separato. In caso di errore il runner visualizza il log della coda insieme alla sessione in claude.ai/code. A meno che non sia stato avviato il runner con [`--remove-session-state`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags), mantiene anche il log di una sessione fallita su disco e stampa il suo percorso nel log del runner.

### When the runner exits

Non riavviare un [runner on-demand](https://code.claude.com/docs/it/self-hosted-environments-configuration.md#on-demand-runners), perché il suo ordine di lavoro è monouso. Un runner che esce subito dopo l'avvio necessita di una gestione diversa da uno che esce per qualsiasi altro motivo.

* **Un'uscita normale**: il runner ha terminato le sue sessioni e si è scaricato, ha raggiunto il suo tempo di ritiro, o gli è stato detto di fermarsi. Riavviarlo in modo che l'ambiente abbia di nuovo capacità. [Runner lifecycle](https://code.claude.com/docs/it/self-hosted-environments#runner-lifecycle) descrive queste uscite.
* **Un avvio fallito**: il runner non può avviarsi con la configurazione o l'host che gli è stato assegnato, quindi esce secondi dopo l'avvio, e esce nello stesso modo ogni volta che lo riavvii. Riavviarlo più velocemente non aiuta. Qualcuno deve leggere il suo output e correggere la causa.
* **Contatto perso**: un runner che non riesce a raggiungere Anthropic per un tempo superiore al suo [lease](https://code.claude.com/docs/it/self-hosted-environments#session-lifecycle), ad esempio mentre il suo host è in sospensione, può essere rimosso dall'ambiente. Quando un runner rimosso si riconnette, esce. Il suo log può mostrare una riga `[runner:fatal]` che contiene `runner record gone server-side` oppure, dopo un'interruzione più lunga, [`poll auth failed`](https://code.claude.com/docs/it/self-hosted-environments-quickstart#set-up-an-environment-and-runner). Il runner non si registra di nuovo da solo, quindi riavvialo.

Configurare il supervisore per riavviare il runner ogni volta che esce, per attendere più a lungo tra i riavvii quando il runner continua a uscire subito dopo l'avvio, e per avvisare qualcuno quando ciò continua a accadere.

In produzione, fai in modo che il tuo supervisore riavvii il runner su un disco pulito, in un nuovo container o in una nuova VM, come raccomandato in [Rafforza il tuo deploy](#harden-your-deployment).

#### Recognize a failed start

Quando il runner non può avviarsi, stampa una riga che dice il motivo, e poi esce. Per la maggior parte delle cause la riga contiene `[runner:fatal]`. Per alcune cause la riga inizia con `error:` invece, incluso quando il runner non può analizzare i suoi flag, non può leggere il segreto dell'ambiente, o non può creare o scrivere nella directory di base. La riga successiva quindi punta a `--help`.

La maggior parte delle righe di log inizia con un timestamp e `[self-hosted-runner]`, che l'esempio seguente omette. Ad esempio, un runner avviato con il git proxy Anthropic e una capacità superiore a uno stampa una riga come questa:

```text theme={null}
[runner:fatal] --use-anthropic-git-proxy requires --capacity 1 (the proxy URL is per-session and linked worktrees share origin). Omit --use-anthropic-git-proxy or set --capacity 1.
```

Cercare la riga nell'output standard e nell'errore standard del runner, nei log del container della piattaforma, o nel file impostato con [`--log-file`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags). Il runner stampa una riga `error:` prima di aprire il file di log, quindi cercarla nel terminale o nei log del container, come [Troubleshooting](#troubleshooting) nota.

Questi aiutano anche quando si legge un avvio fallito:

* **Nessuna riga affatto**: un runner che l'host uccide non stampa nessuno. Se l'output termina senza riga `[runner:fatal]` e senza riga `error:`, controllare se l'host o l'orchestrator ha fermato il processo, ad esempio per aver superato un limite di memoria.
* **Il codice di uscita**: il runner non mette da parte un codice di uscita per gli errori che si ripetono ad ogni avvio. Esce con lo stesso codice per un errore di configurazione, come una combinazione non supportata di flag, e per un errore che può chiarirsi da solo, come l'API che rimane irraggiungibile attraverso i propri tentativi del runner. Basare la decisione di attendere più a lungo su quanto velocemente il runner è uscito, e leggere l'output del runner per imparare il motivo.
* **Un ambiente che sembra sano**: alcuni passaggi di avvio vengono eseguiti dopo che il runner si registra con l'ambiente, come [`--configure-git`](#let-the-runner-configure-git) e la configurazione delle credenziali del git proxy Anthropic. Se uno di questi passaggi fallisce, l'ambiente può continuare a elencare quel runner per alcuni minuti dopo che il processo è uscito, e la pagina **Cloud environments** può leggere **Healthy** mentre nessun runner sta raccogliendo lavoro. Se le sessioni rimangono in coda in un ambiente che sembra sano, controllare se il supervisore sta riavviando il runner.

#### Restart with a wait that grows

Come ottenere un'attesa crescente dipende dal supervisore.

* **Kubernetes**: il [Deployment](#kubernetes) su questa pagina non necessita di alcun cambiamento. Dopo che un container esce, il kubelet per impostazione predefinita attende prima di riavviare il container, e l'attesa cresce ad ogni riavvio fino a un limite. L'attesa ricomincia una volta che il container è stato eseguito per un po' senza uscire.

  Il kubelet applica la stessa attesa dopo un'uscita normale quando il container è stato eseguito solo brevemente. Un runner che si scarica spesso può quindi mostrare anche lo stato `CrashLoopBackOff`, quindi leggere l'output prima di concludere che il runner non può avviarsi. Il comando seguente legge l'output dell'ultima esecuzione da un pod del Deployment:

  ```bash theme={null}
  kubectl logs --previous -n claude-runners deploy/claude-runner
  ```

  Quando l'ultima esecuzione è stata un avvio fallito, la riga `[runner:fatal]` o `error:` è tra le ultime righe dell'output. Per leggere l'ultima esecuzione di un altro pod, nominare quel pod al posto di `deploy/claude-runner`.
* **Docker e Docker Compose**: la [ricetta Compose](#docker-compose) su questa pagina non necessita di alcun cambiamento. Con `restart: always`, Docker attende più a lungo prima di ogni riavvio di un container che continua a uscire, fino a un limite. Sostituire `<container>` con il nome del container nel comando seguente, che legge quante volte Docker ha riavviato il container:

  ```bash theme={null}
  docker inspect --format '{{.RestartCount}}' <container>
  ```

  Il comando stampa un numero. Un numero che continua a salire significa che Docker continua a riavviare il runner.
* **Un'unità systemd**: per impostazione predefinita systemd attende lo stesso `RestartSec` prima di ogni riavvio e non lo allunga, quindi un'unità con `Restart=always` riavvia un runner che non può avviarsi a quell'intervallo ogni volta. Quando gli avvii arrivano abbastanza velocemente da raggiungere il limite della velocità di avvio dell'unità, cinque avvii in 10 secondi per impostazione predefinita, systemd smette di riavviare l'unità. L'unità rimane ferma fino a quando qualcuno non la riavvia, il che systemd consente una volta che l'intervallo del limite di velocità è passato o dopo `systemctl reset-failed`. Poiché `RestartSec` si applica a ogni riavvio, un valore più lungo ritarda anche il riavvio dopo un'uscita normale. Scegliere un valore che bilancia i due, e avvisare sul conteggio dei riavvii dell'unità.
* **Un ciclo shell o il proprio supervisore**: applicare la stessa regola da soli. Iniziare con un'attesa di cinque secondi. Dopo ogni esecuzione che è terminata entro un minuto, raddoppiare l'attesa per il prossimo riavvio, fino a cinque minuti. Dopo un'esecuzione che è durata un minuto o più, tornare a cinque secondi.

#### Check why the runner keeps exiting

Quando il runner è uscito subito dopo l'avvio diverse volte di fila, fermarsi e controllare questi prima di riavviarlo di nuovo.

* **L'ultima riga `[runner:fatal]` o `error:`**: dice il motivo per cui il runner si è fermato. [Troubleshooting](#troubleshooting) elenca le cause comuni.
* **La combinazione di flag**: il [git proxy Anthropic](#use-the-anthropic-git-proxy) richiede `--capacity 1`. Le ricette su questa pagina utilizzano una capacità superiore, quindi abbassarla quando si aggiunge il proxy a una di esse.
* **Cosa può raggiungere l'ambiente del servizio**: se il runner si avvia manualmente e fallisce sotto il supervisore, confrontare l'utente, la directory home, il `PATH`, e il limite di memoria. `--configure-git` e il git proxy Anthropic hanno bisogno di git sul `PATH` e di un `~/.gitconfig` scrivibile.
* **Il segreto dell'ambiente**: se è stato revocato il segreto o è stato digitato male, il runner stampa una riga che contiene `RegisterRunner auth failed`.
* **La scheda Activity dell'ambiente**: aprire l'ambiente e selezionare **Activity**. Se nuovi runner continuano ad apparire lì e nessuno raccoglie lavoro, il supervisore sta riavviando il runner.

Per una diagnosi guidata sull'host del runner, eseguire il [subcommand doctor](#troubleshooting).

### Una sessione viene presa in carico, ma Claude non risponde mai al primo messaggio

Se un runner prende in carico una sessione ma Claude non risponde mai al primo messaggio, e i tuoi runner raggiungono `api.anthropic.com` attraverso un proxy, la causa potrebbe essere il proxy. Anthropic invia a una sessione i suoi messaggi tramite un'unica risposta in streaming che rimane aperta. Se il tuo proxy la mette in buffer, o la chiude prima di inoltrare qualcosa, il primo messaggio non arriva mai. Le altre richieste della sessione continuano a riuscire.

Per risolvere, modifica il modo in cui il proxy gestisce questa richiesta:

```text theme={null}
GET https://api.anthropic.com/v1/code/sessions/<session-id>/worker/events/stream
```

* **Non mettere in buffer la risposta**: lasciala passare man mano che arriva. Il suo content type è `text/event-stream`.
* **Lascia che la risposta resti aperta**: può restare aperta fino a un'ora. Se il proxy ha un timeout di inattività, mantienilo ben al di sopra di 15 secondi, perché per impostazione predefinita Anthropic invia un keep-alive almeno con quella frequenza.

Poi avvia una nuova sessione e verifica che Claude risponda. Aumentare i timeout del runner non serve: attendi solo più a lungo prima che il runner arresti la sessione.

#### Confermare la causa nel log del runner

Questi passaggi valgono per i runner con Claude Code v2.1.287 o successivo, con il valore predefinito `--log-level info`. I runner meno recenti registrano queste righe più tardi, oppure non le registrano affatto.

1. Attendi che trascorra [`--startup-timeout-min`](https://code.claude.com/docs/it/self-hosted-environments-reference#runner-cli-flags), 15 minuti per impostazione predefinita. Il runner arresta quindi la sessione e registra una riga che contiene `no child output for` e termina con `— releasing`.
2. Cerca `SDKStartup: SSE` nel log del runner. Queste righe seguono una riga `finished: interrupted`, alcune righe dopo quella del passaggio 1. La maggior parte non ha un timestamp, quindi un raccoglitore di log potrebbe mostrarle altrove.
3. Trova la riga in questa tabella:

| La riga di log contiene | Cosa fare |
| :- | :- |
| `SSE connect failed (no response in 30s)` o `SSE liveness timeout` | Verifica che il proxy inoltri la richiesta, lasci passare le intestazioni e i dati della risposta man mano che arrivano e non interrompa la connessione |
| `SSE connect failed (HTTP` e un codice di stato | Controlla il log del proxy stesso. Se il proxy ha risposto direttamente alla richiesta, lascia passare la richiesta attraverso di esso. |
| `SSE connect failed (` e qualsiasi altro testo | Il testo tra parentesi è l'errore. Cerca la stessa connessione nel log del proxy stesso. |
| `SSE stream ended by server`, ripetutamente | Controlla il limite di tempo del proxy per le risposte di lunga durata. Una singola riga di questo tipo è normale. |
| `SSE reconnected after` | Cerca una delle altre righe dopo di essa. Questa riga non significa che qualcosa abbia raggiunto la sessione. |

Se il log non contiene nessuna di queste righe, o il log del proxy non mostra nulla di anomalo, [segnala un problema](#report-an-issue).

#### Impedire alla sessione di riprovare

La sessione mantiene il messaggio in sospeso, e un runner che può servire il proprietario della sessione la prende di nuovo in carico. Finché non modifichi il proxy, la sessione resta in attesa nello stesso modo ogni volta. Per evitarlo, archivia la sessione in claude.ai/code.

## Cosa c'è dopo

* [Personalizza le sessioni](https://code.claude.com/docs/it/self-hosted-environments-configuration.md): script wrapper, lifecycle hook, runner on-demand, server MCP, e permessi
* [Testa end to end](https://code.claude.com/docs/it/self-hosted-environments-testing.md): verifica una nuova immagine runner da CI prima di promuoverla
* [Riferimento](https://code.claude.com/docs/it/self-hosted-environments-reference.md): ogni flag CLI, variabile di ambiente, e metrica
