> ## 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.

# Развертывание самостоятельно размещаемых окружений в production

> Запуск самостоятельно размещаемых runners в production: усиление безопасности, контроль сетевого исходящего трафика, учетные данные git, рецепты Kubernetes и Compose, а также устранение неполадок.

<Note>
  Самостоятельно размещаемые окружения находятся в публичной бета-версии на планах Team и Enterprise; раздел [Доступность и ограничения](/docs/ru/self-hosted-environments#availability-and-limitations) охватывает путь включения. Эта страница охватывает запуск флота в production; см. [быстрый старт](/docs/ru/self-hosted-environments-quickstart) для вашего первого runner и сеанса.
</Note>

[Самостоятельно размещаемое окружение](/docs/ru/self-hosted-environments) запускает Claude Code [облачные сеансы](/docs/ru/claude-code-on-the-web) на runners, которые вы развертываете внутри вашей сети, и в production эти сеансы выполняют направленный моделью код от имени всех, кто может отправить сеанс в окружение. Эта страница предназначена для оператора, переводящего рабочее окружение в production. Она проходит через развертывание по порядку: что заблокировать перед подключением реальных систем, исходящий трафик, который требуется флоту, как сеансы аутентифицируются на вашем хосте git, сами рецепты развертывания и что проверить, когда сеансы работают неправильно.

<h2 id="harden-your-deployment">
  Усиление безопасности развертывания
</h2>

Самостоятельно размещаемый runner выполняет произвольный, направленный моделью код на вашей инфраструктуре от имени всех, кто может отправить сеанс в его окружение. Это любой член вашей организации Anthropic и любой, кто может запустить сеанс канала [Claude Tag](https://claude.com/docs/claude-tag/overview) в области, которую Owner направил в окружение. Проработайте каждый элемент перед подключением окружения к production-системам:

* **Эфемерные контейнеры для каждого сеанса**: запускайте каждый процесс runner в свежем контейнере или VM, который уничтожается при выходе процесса, с `--capacity 1` и значением по умолчанию `--drain-grace-sec 0`, чтобы каждый контейнер обслуживал ровно один сеанс. При более высокой емкости или положительном периоде осушения один контейнер обслуживает несколько сеансов от одного [заблокированного владельца](/docs/ru/self-hosted-environments#key-concepts); см. [Runner lifecycle](/docs/ru/self-hosted-environments#runner-lifecycle). Не переиспользуйте файловую систему между перезагрузками runner, кроме намеренной настройки [pre-warmed checkout](#reuse-a-pre-warmed-checkout), и никогда между владельцами.
* **Нет широких учетных данных в образе**: не включайте долгоживущие SSH-ключи, учетные данные облачного провайдера или личные токены доступа, которые предоставляют больше, чем нужно сеансу. Создавайте учетные данные, используемые во время сеанса, такие как токены push или API, для каждого сеанса из вашего [скрипта-обертки](/docs/ru/self-hosted-environments-configuration#wrapper-scripts). Для начального клонирования, которое происходит перед запуском обертки, используйте [`checkout` lifecycle hook](/docs/ru/self-hosted-environments-configuration#checkout) или [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy); см. [Configure git](#configure-git).
* **Держите секрет окружения в тайне от хостов, запускающих сеансы**: секрет окружения может регистрировать runners и получать любой сеанс, поставленный в очередь в окружение. На фиксированном флоте он живет на каждом хосте runner, где код любого сеанса может прочитать файл секрета. Предпочитайте [on-demand runners](/docs/ru/self-hosted-environments-configuration#on-demand-runners), где секрет остается на хосте оркестратора, который никогда не запускает пользовательский код, и каждый runner получает одноразовый наряд на работу, который регистрирует ровно один runner. На фиксированном флоте рассматривайте файл environment-secret как читаемый каждым сеансом и ротируйте секрет после любого подозреваемого компрометирования сеанса.
* **Исходящий трафик по умолчанию запрещен**: ограничьте исходящий трафик контейнеров runner и сеанса на границе вашей собственной сети в каждом окружении; [Default-deny egress](#default-deny-egress) охватывает, что разрешить и почему.
* **Наименьшие привилегии хоста IAM**: вычислительная идентификация, прикрепленная к хосту runner, такая как профиль экземпляра или учетная запись сервиса узла, должна предоставлять только то, что нужно самому runner. Сеансы должны получать свои собственные учетные данные через ваш скрипт-обертку, а не наследовать идентификацию хоста.
* **Блокируйте конечную точку облачных метаданных от сеансов**: чтобы держать сеансы вне хоста идентификации, требуется блокировать их доступ к конечной точке облачных метаданных, и политики исходящего трафика на уровне подсети не перехватывают трафик метаданных link-local, поэтому блокируйте его в самом контейнере:

  * IMDSv2 с пределом хопов в один
  * GKE Workload Identity с скрытием метаданных
  * Явный отказ для `169.254.169.254` в пространстве имен сети контейнера сеанса

  Блокировка применяется к вашему скрипту-обертке и lifecycle hooks, так как они используют контейнер. Аутентифицируйте любой обмен токенами с [session JWT](/docs/ru/self-hosted-environments-identity) против вашего собственного сервиса токенов через разрешенный исходящий трафик, или используйте веб-идентификацию на основе файлов, такую как IAM Roles for Service Accounts (IRSA) на Amazon EKS.
* **Изоляция файловой системы для каждого runner**: каждый процесс runner получает свой собственный рабочий каталог, который никакой другой процесс на хосте не может читать или писать. Сделайте `--hooks-dir`, скрипт-обертку и хост `~/.claude/` доступными только для чтения сеансом, либо встроенными в образ, либо смонтированными только для чтения.
* **Отправка не имеет контроля доступа для каждого окружения**: любой член вашей организации Anthropic может отправить сеанс в любое из его окружений. Если Owner [направляет каналы Claude Tag в окружение](/docs/ru/cloud-environments#set-the-environment-a-claude-tag-channel-uses), любой, кого допускает [параметр доступа Claude Tag](https://claude.com/docs/claude-tag/admins/restrict-access#restrict-who-can-use-claude), может запустить сеансы канала, которые работают там. По умолчанию это любой в подключенном рабочем пространстве Slack, с учетной записью Claude или без нее. Рассматривайте каждый хост runner как доступный для выполнения кода всеми, кто может отправить в него, и размещайте на хосте runner только данные и учетные данные, которые все эти люди имеют право читать. [`--lock-to-account`](/docs/ru/self-hosted-environments-reference#runner-cli-flags) ограничивает, какие сеансы учетной записи выполняет данный хост, но это не сужает, кто может отправить в окружение. Чтобы сделать самостоятельно размещаемые окружения единственным вариантом выбора, [Owner](/docs/ru/cloud-environments#organization-shared-environments) может скрыть окружения, размещаемые Anthropic, для всей организации на странице [**Cloud environments**](https://claude.ai/admin-settings/cloud-environments).
* **Применяйте защиту repo-settings**: выберите режим защиты с помощью [`--confine-repo-settings`](/docs/ru/self-hosted-environments-reference#runner-cli-flags). По умолчанию `warn` регистрирует нарушение и все еще порождает сеанс, `enforce` отказывает в сеансе, а `off` отключает сканирование. Runner сканирует параметры каждого репозитория для:

  * Гранта, который разрешается вне рабочего пространства этого сеанса: запись `additionalDirectories`, правило `Edit`, `Write` или `NotebookEdit` в `permissions.allow`, или запись `sandbox.filesystem.allowWrite` или `allowRead`
  * Непустой блок `env`
  * Переопределение позиции оператора, такое как `sandbox.enabled: false`

  Защита работает независимо от [`--trust-workspace`](/docs/ru/self-hosted-environments-reference#runner-cli-flags) и не охватывает hooks репозитория, `.mcp.json` или правила Bash; см. [Permissions and tool approval](/docs/ru/self-hosted-environments-configuration#permissions-and-tool-approval) для того, где должны находиться эти гранты.

<Note>
  Список разрешенных IP-адресов вашей организации по умолчанию не охватывает трафик self-hosted runner. Не полагайтесь на него как на сетевой контроль для трафика runner или сеанса; вместо этого применяйте default-deny egress на границе вашей собственной сети и свяжитесь с командой вашего аккаунта Anthropic, если вы хотите применение списка разрешенных IP-адресов для вашей организации.
</Note>

<h2 id="network-requirements">
  Требования к сети
</h2>

Runner и порожденные им дочерние сеансы устанавливают исходящие соединения с хостами ниже. Ограничьте исходящий трафик контейнера сеанса этими хостами и конкретными внутренними сервисами, которые сеансам нужно достичь; [Default-deny egress](#default-deny-egress) охватывает как и почему.

Эти хосты всегда требуются:

| Хост                                                                | Порт                                     | Используется для                                                                                                                                                                                                                                                                                                                                                                        |
| :------------------------------------------------------------------ | :--------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api.anthropic.com`                                                 | 443, HTTPS; WSS только для SCM connector | Плоскость управления runner и потоковая передача сеанса, вывод модели, флаги функций, аналитика продукта, [JWKS](/docs/ru/self-hosted-environments-identity) получение ключей, подпись коммитов, git proxy при установке `--use-anthropic-git-proxy` и туннель [SCM connector](/docs/ru/self-hosted-environments-reference#scm-connector-flags) оркестратора при установке `--scm-connector-host` |
| Ваш git-хост, такой как `github.com` или ваш хост GitHub Enterprise | 443 или 22                               | Клонирование и push репозиториев. Не требуется, если runner использует `--use-anthropic-git-proxy`, который маршрутизирует трафик git через `api.anthropic.com`.                                                                                                                                                                                                                        |

Требуются ли эти хосты, зависит от вашей конфигурации:

| Хост                                 | Порт | Когда требуется                                                                                                                                                                                                                                                                                                         |
| :----------------------------------- | :--- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `downloads.claude.ai`                | 443  | Во время установки, когда вы устанавливаете или обновляете Claude Code на хосте с помощью встроенного установщика; сам скрипт `install.sh` обслуживается с `claude.ai`. Во время выполнения сеанса только когда сеансы устанавливают плагины из официального рынка Anthropic.                                           |
| `storage.googleapis.com`             | 443  | Во время выполнения сеанса для количества установок плагинов и метаданных, показанных в `/plugin`.                                                                                                                                                                                                                      |
| `code.claude.com` и `claude.com`     | 443  | Поиск документации встроенным агентом claude-code-guide и предварительно одобренные запросы WebFetch во время сеансов. Блокировка этих хостов влияет только на поиск документации.                                                                                                                                      |
| `*.frame.claudeusercontent.com`      | 443  | Только когда [инструмент Artifact](/docs/ru/artifacts#availability) доступен для сеансов в вашей организации; значения по умолчанию варьируются по плану, согласно таблице доступности там. Установите `CLAUDE_CODE_DISABLE_ARTIFACT=1` на runner, чтобы держать инструмент отключенным независимо от параметра организации. |
| `registry.npmjs.org`                 | 443  | Когда сеанс устанавливает плагин, как для получения пакетов плагинов npm-source, так и для установки зависимостей Node.js плагина, или когда запускается MCP-сервер, запущенный `npx`                                                                                                                                   |
| `http-intake.logs.us5.datadoghq.com` | 443  | Метрики операций Anthropic. Только когда установлено `CLAUDE_CODE_BYOC_ENABLE_DATADOG=1`; отключено по умолчанию в самостоятельно размещаемых окружениях.                                                                                                                                                               |
| `browser-intake-us5-datadoghq.com`   | 443  | Загрузки отчетов об ошибках Anthropic, отправляемые только когда [отчетность об ошибках](/docs/ru/data-usage#telemetry-services) включена для учетной записи сеанса. Подавляется `DISABLE_ERROR_REPORTING=1` или `DISABLE_TELEMETRY=1`.                                                                                      |

Runner не достигает `statsig.anthropic.com`, `*.sentry.io`, `claude.ai` или `platform.claude.com`. Эти хосты появляются в некоторых старых контрольных списках корпоративной сети, но вам не нужно добавлять их в список разрешений для трафика runner или сеанса: получение флагов функций идет на `api.anthropic.com`, и runner аутентифицируется с помощью секрета окружения, а не интерактивного OAuth. Два потока на стороне хоста достигают `claude.ai`, поэтому запускайте их с хоста, чей исходящий трафик это позволяет, а не расширяйте исходящий трафик контейнера сеанса: одностроковый установщик получает `install.sh` с `claude.ai` во время установки, и интерактивный `claude auth login`, который используют [guided setup](/docs/ru/self-hosted-environments-quickstart#set-up-an-environment-and-runner), режим `doctor` с входом и [CI dispatch](/docs/ru/self-hosted-environments-testing#authenticate-from-ci), входит через `claude.ai`, `claude.com` и `platform.claude.com`. `mcp-proxy.anthropic.com` тоже не требуется: самостоятельно размещаемые сеансы его не используют, и доставка коннекторов claude.ai вашей организации в сеансы, когда это включено для вашей организации, маршрутизируется через `api.anthropic.com`. См. [MCP servers](/docs/ru/self-hosted-environments-configuration#mcp-servers).

<h3 id="default-deny-egress">
  Default-deny egress
</h3>

Развертывайте контейнеры runner и сеанса в сегменте сети или пространстве имен, чей исходящий трафик ограничен хостами в [таблице требований к сети](#network-requirements), вашим git-хостом и конкретными внутренними сервисами, которые сеансам нужно достичь. Продукт не может проверить или применить это, поэтому применяйте это на границе вашей собственной сети в каждом окружении. Код сеанса направлен моделью и может попытаться подключиться к произвольным хостам; default-deny egress на сетевом уровне ограничивает, где эти попытки могут приземлиться. Это применяется независимо от режима разрешений: набор инструментов, одобренный по умолчанию, уже включает `Bash`, поэтому исходящий трафик shell работает без подсказки даже без [auto mode](/docs/ru/self-hosted-environments-configuration#permissions-and-tool-approval).

Для деталей о том, какую телеметрию излучает каждый сеанс и как ее отключить, см. [Telemetry](/docs/ru/self-hosted-environments-reference#telemetry).

<h3 id="authenticate-to-an-egress-proxy">
  Аутентификация на исходящий прокси
</h3>

Некоторые корпоративные исходящие прокси требуют заголовок `Proxy-Authorization` на каждом соединении. Токен в этом заголовке часто ротируется слишком быстро, чтобы писать в URL прокси, который вы устанавливаете в `HTTPS_PROXY`. Установите `HTTPS_PROXY` или `HTTP_PROXY` на URL вашего прокси как обычно, затем установите `--proxy-authorization-command` или `--proxy-authorization-file`, чтобы сказать runner, где читать значение заголовка. Оба флага требуют Claude Code v2.1.238 или позже.

<h4 id="choose-where-the-proxy-authorization-value-comes-from">
  Выберите, откуда берется значение `Proxy-Authorization`
</h4>

Выберите флаг, который соответствует тому, как вы производите токен `Proxy-Authorization`:

* **[`--proxy-authorization-command <command>`](/docs/ru/self-hosted-environments-reference#runner-cli-flags)**: выберите это для токена, который вы генерируете по требованию. Runner запускает команду shell и использует ее обрезанный stdout как значение заголовка, например `Bearer <token>`.
* **[`--proxy-authorization-file <path>`](/docs/ru/self-hosted-environments-reference#runner-cli-flags)**: выберите это для токена, который другой процесс ротирует на месте. Runner читает файл и использует его обрезанное содержимое как значение заголовка.

<h4 id="configurations-the-runner-refuses-to-start-with">
  Конфигурации, с которыми runner отказывается запускаться
</h4>

Каждый флаг также имеет форму переменной окружения, указанную рядом с ним в [справочнике флагов CLI runner](/docs/ru/self-hosted-environments-reference#runner-cli-flags). Перед тем как runner свяжется с вашим прокси или плоскостью управления, он проверяет флаги и их переменные и отказывается запускаться в трех случаях:

* **Оба флага установлены**: один флаг плюс переменная окружения другого флага считается установкой обоих.
* **Нет URL прокси**: ни `HTTPS_PROXY` ни `HTTP_PROXY` не содержат URL `http://` или `https://`. Runner читает обе переменные в верхнем или нижнем регистре и не консультирует `ALL_PROXY`.
* **Любой флаг передан подкоманде orchestrator**: `self-hosted-runner orchestrator` не принимает флаги или их переменные окружения. Вместо этого передайте флаг каждому runner, который запускает оркестратор.

<h4 id="what-the-runner-changes-while-a-proxy-authorization-flag-is-set">
  Что runner изменяет, пока установлен флаг proxy-authorization
</h4>

С установленным любым флагом runner запускает собственный слушатель и отправляет трафик прокси от себя, своих lifecycle hooks и своих сеансов через этот слушатель. Слушатель добавляет заголовок `Proxy-Authorization` на пути к вашему прокси.

* **Слушатель**: слушатель является forward proxy на `127.0.0.1`. Runner запускает слушатель перед регистрацией в плоскости управления и выходит при запуске, если слушатель не может запуститься.
* **Переменные прокси**: runner переписывает любой из `HTTPS_PROXY` и `HTTP_PROXY`, который вы установили, чтобы он указывал на слушатель. Это переписанное значение достигает самого runner, его lifecycle hooks и каждого сеанса, который он запускает.
* **Ротация токена**: ротированный токен вступает в силу без перезагрузки. Для каждого соединения, которое слушатель открывает для вашего прокси, runner запускает вашу команду или читает ваш файл снова и добавляет результат как заголовок.
* **Окружение сеанса**: сеанс достигает вашего прокси только через слушатель. В окружении каждого сеанса runner удаляет `ALL_PROXY`, удаляет любое написание `HTTPS_PROXY` или `HTTP_PROXY`, которое вы не установили, и закрепляет `NO_PROXY` на собственное значение runner.
* **Логи**: runner никогда не регистрирует значение заголовка.

<h2 id="configure-git">
  Конфигурирование git
</h2>

Runner управляет checkouts репозитория, но по умолчанию не конфигурирует идентификацию git или учетные данные. Вы контролируете образ runner и окружение процесса, поэтому вы контролируете конфигурацию git. Выберите один из двух подходов:

* **Позвольте runner конфигурировать git**: запустите runner с `--configure-git`, чтобы он написал ту же идентификацию и конфигурацию подписи коммитов, которые используют сеансы, размещаемые Anthropic
* **Отправьте конфигурацию git в вашем образе**: установите идентификацию и учетные данные push самостоятельно, например, чтобы коммитить под вашей собственной идентификацией бота

Минимальные версии Git на хосте runner: [`--configure-git`](#let-the-runner-configure-git) подпись коммитов SSH требует Git 2.34 или новее, [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) требует 2.32 или новее, и возобновление сеансов из веток, отправленных [`--push-outcome-on-release`](/docs/ru/self-hosted-environments-reference#runner-cli-flags), требует 2.29 или новее. Git 2.24 достаточно, если вы опустите все три и управляете идентификацией git самостоятельно.

<h3 id="let-the-runner-configure-git">
  Позвольте runner конфигурировать git
</h3>

Запустите runner с `--configure-git` или установите `SELF_HOSTED_RUNNER_CONFIGURE_GIT=1`, чтобы он написал глобальную конфигурацию git при запуске:

* `user.name = Claude` и `user.email = noreply@anthropic.com`, соответствуя сеансам, размещаемым Anthropic
* Подпись коммитов и тегов в формате SSH, маршрутизированная через управляемый runner shim, который подписывает каждый коммит через сервис подписи Anthropic, используя собственные учетные данные сеанса. Подписи проверяются на GitHub против опубликованного SSH-ключа подписи Anthropic.
* `push.negotiate = true`, чтобы git спросил ваш git-хост, какие коммиты он уже имеет, перед упаковкой push. Требует Claude Code v2.1.257 или позже.
* `core.hooksPath`, указывающий на управляемый runner каталог hooks. Его hooks `commit-msg` и `prepare-commit-msg` добавляют трейлер `Co-authored-by:` для создателя сеанса к каждому коммиту, построенный из email в [`CCR_SESSION_ACCOUNT_EMAIL`](/docs/ru/self-hosted-environments-configuration#wrapper-scripts) и опущенный, когда эта переменная не установлена. Если ваш образ уже устанавливает `core.hooksPath`, runner оставляет вашу настройку на месте, пропускает установку этих hooks и печатает предупреждение `[runner:git]`.

Подпись коммитов требует git 2.34 или новее; runner проверяет при запуске и выходит с ошибкой, если ваш git старше. Этот флаг не конфигурирует учетные данные push, которые вы все еще предоставляете в образе.

<h3 id="ship-git-config-in-your-image">
  Отправьте конфигурацию git в вашем образе
</h3>

Идентификация git требуется для любого коммита. Установите ее системно в вашем Dockerfile, чтобы конфигурация применялась независимо от того, какой пользователь запускает процесс runner:

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

Без идентификации `git commit` не удается с `Please tell me who you are` и сеансы не могут прогрессировать. Вы можете использовать вместо этого вашу собственную идентификацию бота; runner не переопределяет эти значения.

Не запекайте долгоживущие или широко охватывающие учетные данные push в общий образ runner: учетные данные в образе доступны каждому сеансу, который запускает образ, кто бы его ни запустил. Вместо этого создавайте короткоживущий, наименее охватывающий токен для каждого сеанса из вашего [скрипта-обертки](/docs/ru/self-hosted-environments-configuration#wrapper-scripts), используя идентификацию создателя сеанса, декодированную из session JWT. Сочетайте это с эфемерным контейнером для каждого сеанса, который требует `--capacity 1`, чтобы никакие учетные данные не пережили сеанс, который их создал; см. [раздел усиления безопасности](#harden-your-deployment).

Если вы должны конфигурировать учетные данные push на уровне образа, например для ключа развертывания только для чтения, ограничьте их так плотно, как позволяет ваш git-хост:

* SSH-ключ развертывания, ограниченный одним репозиторием с переписью `url.<base>.insteadOf`
* `credential.helper`, который возвращает минимально охватывающий токен
* `GIT_SSH_COMMAND`, указывающий на узко охватывающий ключ

Какой бы механизм вы ни конфигурировали, он должен работать без подсказки, потому что встроенное клонирование и получение runner отключают подсказки, которые git, SSH и Git Credential Manager иначе показали бы:

* Runner устанавливает `GIT_TERMINAL_PROMPT=0`, чтобы git не спрашивал имя пользователя или пароль.
* Runner запускает SSH с `BatchMode=yes`, добавленным к вашему `GIT_SSH_COMMAND`, если вы его установили, чтобы SSH не спрашивал парольную фразу или подтверждение хоста.
* Runner устанавливает `GCM_INTERACTIVE=never`, чтобы Git Credential Manager не открывал диалог входа.
* Runner очищает `core.askPass`, поэтому, если вы используете помощника askpass, установите его через переменную окружения `GIT_ASKPASS` вместо этого.

Если ваш git-хост отклоняет учетные данные или вы не конфигурировали их, runner повторяет попытку несколько раз, а затем не подготавливает репозиторий. Runner не передает эти параметры в окружение сеанса.

Если каталоги checkout принадлежат другому uid, чем процесс runner, git отказывается работать с ними; добавьте `safe.directory`:

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

<h3 id="use-the-anthropic-git-proxy">
  Используйте прокси git Anthropic
</h3>

Запустите runner с `--use-anthropic-git-proxy` или установите `CLAUDE_RUNNER_USE_GIT_PROXY=1`, чтобы он клонировал через прокси git Anthropic, аутентифицированный с помощью собственного короткоживущего токена сеанса. Для обычных пользовательских сеансов прокси использует токен GitHub или GitHub Enterprise OAuth, сохраненный для создателя сеанса; для сеансов бота и агента он использует токен установки GitHub App вашей организации. В любом случае образ runner не нуждается в учетных данных git вообще: нет SSH-ключей, нет помощника учетных данных, нет `.netrc`. Это тот же путь аутентификации, который используют окружения, размещаемые Anthropic.

Прокси требует `--capacity 1`, потому что URL прокси зависит от сеанса, и git 2.32 или новее, потому что более старый git игнорирует механизм конфигурации, который прокси использует для изоляции сеансов друг от друга. Runner отказывается запускаться, если какое-либо требование не выполнено. Поскольку прокси получает со стороны Anthropic, ваш git-хост должен быть доступен из инфраструктуры Anthropic, то же требование, что и для сеансов, размещаемых Anthropic; для git-хоста, который маршрутизируется только внутри вашей сети, используйте вместо этого [`checkout` lifecycle hook](/docs/ru/self-hosted-environments-configuration#checkout). Каждый процесс runner обрабатывает один сеанс за раз, поэтому запускайте больше реплик для параллелизма. Когда прокси включен, `--git-host-rewrite` и `--git-ssh-rewrite` не имеют эффекта: URL прокси указывает на `api.anthropic.com`, а не на ваш git-хост.

<h3 id="rewrite-git-urls-for-private-networks">
  Переписывайте URL git для приватных сетей
</h3>

URL репозитория приходят из плоскости управления как HTTPS с именем хоста вашего git-хоста; для GitHub Enterprise это имя хоста, которое вы конфигурировали для [интеграции GitHub Enterprise](/docs/ru/github-enterprise-server) в параметрах администратора Claude Code на claude.ai. Два повторяемых флага переписывают эти URL перед клонированием:

* `--git-host-rewrite <from>=<to>`: для split-horizon DNS, где Anthropic достигает вашего git-хоста через внешнее имя хоста, но runners должны использовать внутреннее
* `--git-ssh-rewrite <host>`: для git-хостов, которые принимают только SSH, переписывая `https://<host>/owner/repo` на `git@<host>:owner/repo`

Переписывание хоста работает первым, поэтому перечислите внутреннее имя хоста в `--git-ssh-rewrite`, если вам нужны оба. Для полного контроля над checkout используйте [`checkout` lifecycle hook](/docs/ru/self-hosted-environments-configuration#checkout).

<h2 id="build-the-runner-image">
  Постройте образ runner
</h2>

Anthropic не публикует предварительно построенный образ runner. Постройте свой собственный вокруг бинарного файла `claude`, наслаивая любой набор инструментов, который нужен вашим репозиториям: языковые среды выполнения, компиляторы, менеджеры пакетов и [MCP](/docs/ru/mcp) sidecars.

Рецепты ниже используют `--capacity 4`, поэтому один контейнер обслуживает до четырех одновременных сеансов от одного заблокированного владельца. Это не обеспечивает изоляцию контейнера для каждого сеанса в [разделе усиления безопасности](#harden-your-deployment): перед подключением окружения к production-системам либо запустите рецепты на `--capacity 1` с одним контейнером на сеанс, либо используйте [on-demand runners](/docs/ru/self-hosted-environments-configuration#on-demand-runners), которые также держат секрет окружения вне хостов, запускающих сеансы.

Этот Dockerfile является минимальной отправной точкой:

```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 \
 && 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"]
```

Замените `linux-x64` на `linux-arm64`, если ваши узлы ARM, или на `linux-x64-musl` или `linux-arm64-musl` на образе на основе musl, таком как Alpine; см. [Alpine Linux setup](/docs/ru/setup#alpine-linux-and-musl-based-distributions) для дополнительных пакетов, которые нужны образам musl. URL является стандартным местоположением выпуска Claude Code, поэтому вы можете проверить загруженный бинарный файл против подписанного манифеста выпуска, как описано в [Binary integrity and code signing](/docs/ru/setup#binary-integrity-and-code-signing). Постройте образ с версией Claude Code 2.1.224 или позже, затем отправьте его в ваш реестр и ссылайтесь на него в рецептах ниже:

```bash theme={null}
docker build --build-arg CLAUDE_CODE_VERSION=2.1.224 -t <your-registry>/claude-runner:latest .
```

<h2 id="size-cpu-and-memory-for-sessions">
  Размер CPU и памяти для сеансов
</h2>

Размер контейнера или хоста runner для сеансов, которые он запускает, а не для самого процесса runner. Runner сам опрашивает работу, подготавливает checkout каждого сеанса, запускает ваши [lifecycle hooks](/docs/ru/self-hosted-environments-configuration#lifecycle-hooks) и запускает и контролирует процессы сеанса. Нагрузка исходит от сеансов: каждый из них является процессом Claude Code плюс все, что он запускает, такое как сборки, наборы тестов, установки пакетов и [MCP servers](/docs/ru/mcp).

Для одного сеанса начните со следующих значений, указанных как запросы и лимиты Kubernetes или эквивалент вашей платформы, и рассматривайте их как отправную точку, а не требование:

* **Память**: запрос и лимит 4 ГиБ каждый, что соответствует минимуму 4 ГБ в [системных требованиях](/docs/ru/setup#system-requirements) Claude Code. Держите их равными, чтобы планировщик учитывал полную память контейнера. Когда контейнер достигает своего лимита памяти, ядро убивает процессы внутри него, что может завершить сеанс в середине задачи.
* **CPU**: запрос 2 CPU и лимит 4 CPU, чтобы сеанс мог всплеснуть выше запроса во время сборок. Ядро дросселирует контейнер на его лимите CPU, а не убивает процессы в нем, поэтому сеансы на лимите работают медленнее, но продолжают работать.

В спецификации контейнера Kubernetes установите эти начальные значения с помощью следующего блока `resources`:

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

Сборки и тесты обычно являются самой большой и наиболее переменной частью нагрузки сеанса, поэтому запустите репрезентативную сборку вашего репозитория, измерьте его пиковый CPU и память и поднимите любое начальное значение, которое не оставляет места для процесса Claude Code на вершине этого пика.

Runner использует `--capacity` для ограничения количества сеансов, которые он запускает одновременно. Он не делит CPU или память между ними, поэтому сеансы на runner делят CPU и память контейнера. Чтобы ограничить долю одного сеанса, применяйте лимиты из вашего [скрипта-обертки](/docs/ru/self-hosted-environments-configuration#wrapper-scripts). То, что дать одному контейнеру, поэтому зависит от того, сколько сеансов он обслуживает одновременно:

* **Один сеанс на runner**: дайте каждому контейнеру значения одного сеанса. Используйте эту размер на `--capacity 1`, которую [раздел усиления безопасности](#harden-your-deployment) рекомендует, и для [on-demand runners](/docs/ru/self-hosted-environments-configuration#on-demand-runners), где вы устанавливаете значения на рабочую нагрузку, которую ваш [`spawn-runner` hook](/docs/ru/self-hosted-environments-configuration#the-spawn-runner-hook) отправляет, такую как шаблон pod Kubernetes Job.
* **Несколько сеансов на runner**: на `--capacity` выше одного, умножьте значения одного сеанса на емкость, потому что до такого количества сеансов может работать в контейнере одновременно. Рецепты [Kubernetes](#kubernetes) и [Docker Compose](#docker-compose) запускают `--capacity 4` без лимитов CPU или памяти, поэтому добавьте лимиты, размер которых соответствует емкости, которую вы запускаете.

<h2 id="kubernetes">
  Kubernetes
</h2>

Runner обслуживает `GET /healthz` на порту 8080 по умолчанию, конфигурируемом с помощью `--health-port`, поэтому зонды Kubernetes работают без дополнительной настройки. Конечная точка возвращает `200` всякий раз, когда процесс живой, поэтому зонды ниже обнаруживают мертвый процесс, а не застрявший; чтобы поймать runner, который перестал опрашивать, установите оповещение на серию `last_poll_age_seconds` из [`/metrics`](/docs/ru/self-hosted-environments-reference#prometheus-metrics). Развертывание ниже монтирует секрет окружения из Kubernetes Secret, указывает зонды liveness и readiness на `/healthz` и устанавливает период завершения 90 секунд. См. [Shutdown timing](#shutdown-timing) для того, почему период завершения имеет значение.

Манифест не устанавливает `resources` CPU или памяти на контейнер runner. Добавьте блок, размер которого соответствует емкости, которую вы запускаете, как описано в [Size CPU and memory for sessions](#size-cpu-and-memory-for-sessions).

```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"
          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
```

Развертывание выше живет в пространстве имен `claude-runners`. Сначала создайте пространство имен:

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

Создайте поддерживающий Secret из локального файла, содержащего значение, которое вы скопировали на шаге [**Copy environment key**](/docs/ru/self-hosted-environments-quickstart#set-up-an-environment-and-runner) в пользовательском интерфейсе администратора, чтобы секрет никогда не появлялся в истории вашей shell. Запустите `(umask 077 && cat > ./environment-secret)`, вставьте секрет, нажмите Enter, затем Ctrl-D. Затем создайте Secret и удалите файл:

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

<h2 id="docker-compose">
  Docker Compose
</h2>

Сервис Compose ниже перезапускает runner всякий раз, когда он выходит, что охватывает как сбои, так и нормальный выход после осушения. Политика перезагрузки Docker перезапускает тот же контейнер с его записываемым слоем нетронутым, поэтому runner возвращается на переиспользованной файловой системе, а не на свежей, которую рекомендует [позиция усиления безопасности](#harden-your-deployment); используйте этот рецепт для оценки, и для production либо пересоздайте контейнер за запуск, либо используйте оркестратор, который это делает.

```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"
    secrets:
      - environment-secret
    restart: always
    stop_grace_period: 90s

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

<h2 id="shutdown-timing">
  Время завершения работы
</h2>

При получении `SIGTERM` runner прекращает принимать новую работу и, если вы не установили [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal), ждёт до `--drain-wait-sec`, по умолчанию ноль, чтобы завершить выполняемые turns, завершает дерево процессов каждой сессии и запускает [`post-session` lifecycle hook](/docs/ru/self-hosted-environments-configuration#post-session). Это дерево процессов включает команды, которые Claude всё ещё выполнял в сессии.

Полный путь drain требует до `--session-stop-grace-sec` + `--drain-wait-sec` + `--post-session-hook-timeout-sec`, плюс 15 секунд фиксированных накладных расходов на очистку процессов, плюс ещё 30 секунд, когда установлен [`--push-outcome-on-release`](/docs/ru/self-hosted-environments-reference#runner-cli-flags). При значениях по умолчанию это 80 секунд, и runner логирует общее время при запуске. Сессии drain параллельно в рамках этого одного бюджета, поэтому общее время не растёт с `--capacity`.

При значении по умолчанию `--drain-wait-sec 0` rolling restart прерывает выполняемые turns; каждая сессия возобновляется на другом runner, теряя неотправленную работу, как описано в разделе [Известные проблемы](#additional-limitations). Установите `--drain-wait-sec` и увеличьте период grace, чтобы соответствовать ему, чтобы позволить turns завершиться в первую очередь.

На протяжении всего этого пути runner продолжает отправлять heartbeat на control plane с нулевой ёмкостью, поэтому lease сессии не истекает и не переставляется на другой runner, пока hook `post-session` всё ещё записывает неподтверждённую работу. Heartbeat останавливается непосредственно перед тем, как runner отменяет регистрацию.

Дайте runner по крайней мере общее время, которое он логирует при запуске, прежде чем хост его остановит. Где вы это установите, зависит от того, как ваши хосты останавливаются:

* **С периодом grace `SIGTERM`**: установите `terminationGracePeriodSeconds` на Kubernetes, `stop_grace_period` на Docker Compose или эквивалент вашего оркестратора по крайней мере на это общее время. Значение по умолчанию Kubernetes в 30 секунд короче, чем путь drain runner, поэтому Kubernetes останавливает pod до того, как runner завершит drain.
* **С [`--retire-at`](/docs/ru/self-hosted-environments-reference#runner-cli-flags)**: установите размер margin между временем retire и временем остановки хоста, чтобы охватить типичные turns, плюс background-task hold, который описывает [Runner lifecycle](/docs/ru/self-hosted-environments#runner-lifecycle), плюс то же самое общее время. Вычислите время retire при каждом запуске, например `date +%s` плюс предполагаемое время жизни runner.
* **С [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal)**: добавьте две дополнительные части к общему времени drain-path. Первая — это минуты, которые вы настраиваете. Вторая — это post-release grace, который описывает [Defer the drain past the first signal](#defer-the-drain-past-the-first-signal), 75 секунд при значениях по умолчанию. Когда флаг установлен, runner также выводит объединённую цифру при запуске, после общего времени drain-path.

<h3 id="defer-the-drain-past-the-first-signal">
  Defer the drain past the first signal
</h3>

Установите [`--defer-shutdown-max-min <n>`](/docs/ru/self-hosted-environments-reference#runner-cli-flags), если вы хотите, чтобы runner, который вы перезапускаете, продолжал обслуживать сессии, которые он удерживает, до `n` минут, вместо того чтобы drain их при первом сигнале. При первом `SIGTERM` или `SIGINT` runner прекращает принимать новую работу и продолжает обслуживать сессии, которые он удерживает. Он продолжает опрашивать, чтобы control plane не переставлял эти сессии. Требует Claude Code v2.1.238 или позже.

<h4 id="what-happens-to-the-sessions-the-runner-holds-after-the-first-signal">
  Что происходит с сессиями, которые runner удерживает после первого сигнала
</h4>

На первых двух этапах, следующих за сигналом, runner выпускает сессии, и выпущенная сессия возобновляется на свежем runner, когда его пользователь отправляет следующее сообщение. Отсчитывая от первого сигнала, runner проходит через три этапа:

* **В течение первых `n` минут**: runner обслуживает свои сессии нормально и продолжает применять `--startup-timeout-min` и `--kill-session-after-min`. Если вы также установили [`--release-idle-session-min`](/docs/ru/self-hosted-environments-reference#runner-cli-flags), runner выпускает любую сессию, пользователь которой был неактивен столько времени; без этого неактивные сессии остаются на runner.
* **Когда истекают `n` минут**: runner выпускает каждую сессию, которую он всё ещё удерживает, неактивную или нет. Runner ждёт, пока turn сессии mid-turn завершится, и до 60 секунд больше для background tasks turn, прежде чем выпустить эту сессию.
* **Когда истекает post-release grace**: runner drain любые сессии, которые он всё ещё удерживает, и control plane переставляет каждую drained сессию на другой runner сразу же. Post-release grace начинается, когда истекают `n` минут, и составляет 75 секунд при значениях по умолчанию. Если вы установили `--drain-wait-sec` выше 60 секунд, post-release grace составляет `--drain-wait-sec` плюс 15 секунд вместо этого.

На любом этапе runner выходит с кодом 0, как только он не удерживает никаких сессий. Второй сигнал сокращает этапы: runner drain немедленно, как он это делает при первом сигнале без `--defer-shutdown-max-min`. Как только drain находится в процессе, следующий сигнал force-exits runner. Это верно, независимо от того, начал ли drain второй сигнал или истекла post-release grace.

<h4 id="size-the-stop-timeout">
  Установите timeout остановки
</h4>

Дайте timeout остановки вашего хоста по крайней мере сумму трёх частей: `n` минут, которые вы настраиваете, post-release grace и полный drain path, который описывает [Shutdown timing](#shutdown-timing). При настройках по умолчанию post-release grace составляет 75 секунд, а drain path составляет 80 секунд, поэтому выделите `n` минут плюс 155 секунд. Runner выводит эту сумму при запуске всякий раз, когда установлен `--defer-shutdown-max-min`.

Если timeout остановки истекает до того, как runner завершит работу, хост убивает runner. Сессии, которые он всё ещё удерживает, не получают hook `post-session`. Runner не отменяет регистрацию, и control plane переставляет сессии примерно через минуту. Если вы не можете дать timeout остановки эту сумму, оставьте `--defer-shutdown-max-min` неустановленным, чтобы runner drain при первом сигнале вместо этого.

<h3 id="what-reaches-a-running-post-session-hook">
  Что достигает работающего post-session hook
</h3>

Hook `post-session` и дочерний процесс Claude сессии каждый запускаются в своей собственной POSIX группе процессов, отдельно от runner, поэтому механизмы остановки достигают их по-разному:

* **`SIGTERM` пока runner уже drain**: force-exits runner немедленно, пропуская всё, что остаётся от пути drain. Без [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal), это второй `SIGTERM`, который получает runner. Ничто не сигнализирует работающему hook `post-session`, поэтому на голом хосте, где init процесс усыновляет orphans, он завершается самостоятельно, но без надзора: его бюджет timeout больше не применяется, и запись в закрытую трубу логов может убить его с `SIGPIPE`, поэтому hook, который должен пережить forced exit там, должен перенаправить свой собственный вывод в файл. В рецептах контейнеров на этой странице runner является PID 1 контейнера и его выход завершает контейнер, и при systemd по умолчанию `KillMode=control-group` cgroup-wide kill достигает hook тоже, как описывает запись **Cgroup-wide kills**; в обоих случаях рассматривайте forced exit как фатальный для hook и полагайтесь на период grace вместо этого.
* **Process-group-wide signals**, такие как `kill -- -<pid>` в скрипте-обёртке, shell job control или group-wide watchdog: достигают runner и subprocess mid-`checkout`-hook, который остаётся group-attached намеренно, но не mid-run hook `post-session` или дочерний процесс сессии.
* **Cgroup-wide kills**, такие как systemd по умолчанию `KillMode=control-group` или `SIGKILL`, который Kubernetes доставляет всему контейнеру, когда истекает `terminationGracePeriodSeconds`: достигают всего, включая hook. Изоляция process-group не защищает от этого, поэтому период grace должен охватывать полный путь drain.
* **Собственный timeout hook**: когда hook превышает `--post-session-hook-timeout-sec`, runner отправляет `SIGTERM` на всю группу процессов hook, затем `SIGKILL` две секунды спустя, поэтому worker, который fork hook, такой как tar, rsync или git, завершается с оболочкой-обёрткой вместо того, чтобы выжить как orphan. Надзор runner заканчивается, как только stdio hook закрывается: worker, который перенаправил свой собственный вывод в файл и пережил этап `SIGTERM`, находится вне досягаемости runner.

Когда начинается drain, и снова при forced exit, runner логирует, сколько hooks `post-session` всё ещё работают, поэтому вы можете отличить тихий drain от того, который находится mid-snapshot.

<h2 id="keep-the-base-directory-and-capacity-identical-across-runners">
  Держите базовый каталог и емкость идентичными на всех runners
</h2>

Если runner умирает в середине сеанса, сервер переставляет сеанс в очередь и другой runner в окружении его подхватывает. Этот runner выводит путь checkout из своего собственного `--base-dir` и `--capacity`: `--capacity 1` проверяет прямо под `--base-dir`, и `--capacity` выше `1` использует вместо этого per-session worktrees. Когда runners в одном окружении используют разные значения для любого флага, рабочий каталог возобновленного сеанса изменяется, и абсолютные пути, которые агент записал ранее, в редактированиях, вызовах инструментов или своих собственных заметках, указывают на местоположение, которое больше не существует.

Используйте одинаковые `--base-dir` и `--capacity` на каждом runner в окружении и не используйте значение для каждого хоста, такое как ID экземпляра или имя хоста.

Базовый каталог по умолчанию `/workspace`, с исключением, которое записывает строка справочника [`--base-dir`](/docs/ru/self-hosted-environments-reference#runner-cli-flags). Runner нуждается в доступе на запись к нему. При запуске, перед регистрацией, runner создает каталог и подтверждает, что может писать в него, и выходит с `cannot create or write to base directory`, когда не может. Runner, запущенный как root, создает по умолчанию `/workspace` сам. Для non-root runner создайте каталог и дайте пользователю runner владение перед запуском runner, или укажите `--base-dir` на каталог, который этот пользователь уже владеет.

<h2 id="reuse-a-pre-warmed-checkout">
  Переиспользуйте pre-warmed checkout
</h2>

Для больших репозиториев клон может доминировать при запуске сеанса. На `--capacity 1` без [`checkout` hook](/docs/ru/self-hosted-environments-configuration#checkout), runner держит один канонический клон на репозиторий на `<base-dir>/<repo-owner>/<repo>` и переиспользует его на сеансы: он получает запрошенный ref, отсоединяет `HEAD` и жестко сбрасывает его, что почти мгновенно, когда мало что изменилось. Чтобы пропустить холодный клон, поставьте клон одним из двух способов:

* **Клон в образе**: постройте клон в образ runner на этом пути. Каждый свежий контейнер затем начинается с теплым клоном без переиспользования диска.
* **Клон на постоянном томе**: на runners, которые вы предварительно блокируете для учетной записи одного пользователя с [`--lock-to-account`](/docs/ru/self-hosted-environments-reference#runner-cli-flags), укажите `--base-dir` на постоянный том, поэтому диск только когда-либо обслуживает эту учетную запись. Pre-locked runner никогда не подхватывает сеансы канала Claude Tag, поэтому этот вариант не применяется к runners, которые их обслуживают.

Что путь переиспользования делает и не гарантирует:

* **Любая форма клона работает**: полный, неглубокий или однозвездный клон на пути используется как есть. Runner никогда не передает `--depth` при получении в существующий клон, поэтому полный pre-warm держит свою полную историю и неглубокий остается неглубоким. `CLAUDE_RUNNER_FETCH_DEPTH` (`full`, `0` или число; по умолчанию 50) контролирует только холодный клон, который runner делает, когда клон еще не существует.
* **Отслеживаемые изменения сбрасываются, неотслеживаемые файлы сохраняются**: каждый сеанс начинается с жесткого сброса, который стирает предыдущие модификации сеанса, но runner никогда не запускает `git clean`, поэтому неотслеживаемые файлы из более ранних сеансов заблокированного владельца остаются в дереве.
* **С git proxy, сброс становится checkout**: с [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), runner дезинфицирует `.git/` клона перед каждым сеансом, сохраняя хранилище объектов, refs и неглубокое состояние, но удаляя индекс, поэтому каждый сеанс платит полный checkout рабочего дерева вместо почти мгновенного сброса; он все еще никогда не переклонирует. Pre-warms подмодулей не поддерживаются под proxy.
* **Длинные клоны не нуждаются в обходном пути**: runner ограничивает каждую операцию git с помощью 120-секундного наблюдателя без прогресса и 30-минутного жесткого лимита, а не плоского тайм-аута, поэтому медленный холодный клон, который продолжает сообщать о прогрессе, завершается.

<h2 id="pin-the-version">
  Закрепите версию
</h2>

Процесс дочернего Claude Code каждого сеанса запускает собственный бинарный файл runner, и runner отключает auto-update внутри сеансов, которые он порождает, поэтому каждый сеанс запускает версию, которую вы установили на хосте или встроили в образ. Обновление на уровне хоста вступает в силу в следующий раз, когда runner запускается.

* **Чтобы держать флот на одной версии**: постройте образ с закрепленной версией или на голом хосте установите конкретную версию и [отключите auto-updates](/docs/ru/setup#disable-auto-updates)
* **Чтобы обновить**: установите более новую версию или пересоздайте образ, затем перезагрузите runners
* **Плагины**: рынки плагинов тоже не auto-update; установите `FORCE_AUTOUPDATE_PLUGINS=1` в окружении runner, чтобы позволить плагинам auto-update, пока бинарный файл остается закрепленным

<h2 id="scale-the-fleet">
  Масштабируйте флот
</h2>

Ваш оркестратор решает, когда добавлять или удалять runners. Из-за [блокировки one-owner-per-runner](/docs/ru/self-hosted-environments#runner-lifecycle), минимальное количество реплик - это количество пользователей и агентов Claude Tag, которых вы ожидаете быть активными одновременно; `--capacity` контролирует параллелизм в пределах сеансов одного владельца, а не на всех владельцах.

Доступны два подхода к масштабированию:

* **Фиксированный флот**: запустите статический набор реплик runner и масштабируйте на [Prometheus metrics](/docs/ru/self-hosted-environments-reference#prometheus-metrics), которые обслуживает каждый runner
* **On-demand runners**: запустите подкоманду `claude self-hosted-runner orchestrator`, которая опрашивает Anthropic для сеансов, которые поставлены в очередь без доступного runner и вызывает ваш hook `spawn-runner` для загрузки одного на сеанс. См. [On-demand runners](/docs/ru/self-hosted-environments-configuration#on-demand-runners).

<h2 id="known-issues-and-limitations">
  Известные проблемы и ограничения
</h2>

Ниже приведены ограничения в этом выпуске с обходными путями, где они существуют.

<h3 id="connector-traffic-leaves-your-network">
  Трафик коннектора покидает вашу сеть
</h3>

Anthropic вызывает инструменты коннектора из своей собственной инфраструктуры, а не из вашего runner. Инструменты коннектора - это коннекторы claude.ai, такие как GitHub, Slack и Linear. Когда Claude использует коннектор в самостоятельно размещаемом сеансе, этот трафик идет через `api.anthropic.com`, а не исходит из границы вашей сети.

Чтобы держать коннектор вне самостоятельно размещаемых сеансов, отфильтруйте его с помощью [`allowedMcpServers` и `deniedMcpServers` параметров политики](/docs/ru/managed-mcp#policy-based-control-with-allowlists-and-denylists). Claude Code применяет эти параметры к коннекторам, которые Anthropic доставляет, а также к серверам, которые вы сеете с хоста runner и серверам, которые пользователи добавляют, поэтому если вы развертываете список разрешений для других серверов, Claude Code блокирует доставленные коннекторы тоже. Чтобы держать коннекторы доступными наряду со списком разрешений на основе URL, добавьте записи, которые соответствуют путям прокси Anthropic для доставленных коннекторов:

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

Если трафик инструмента должен остаться внутри вашей сети, запустите эквивалентные инструменты как локальные MCP-серверы на образе runner вместо этого. См. [MCP servers](/docs/ru/self-hosted-environments-configuration#mcp-servers).

<h3 id="some-sessions-don’t-count-as-idle">
  Некоторые сеансы не считаются неактивными
</h3>

Сеанс, держащий фоновую задачу, которая никогда не заканчивается, не считается неактивным, поэтому `--release-idle-session-min` не выпустит слот этого сеанса. Сеанс, который ждет одобрения, запрошенного изнутри работающего вызова инструмента, тоже не считается неактивным. Всегда устанавливайте `--kill-session-after-min` рядом с ним как жесткий упор, чтобы никакой сеанс не мог держать слот неопределенно долго.

`--kill-session-after-min` является упором для убегающих сеансов. На runner на v2.1.260 или позже, сеанс, который достигает лимита, не завершается сразу. Runner дает ему окно благодати, 15 минут по умолчанию, которое вы можете изменить с помощью [`SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS`](/docs/ru/self-hosted-environments-reference#environment-variable-only-settings):

* Если сеанс ждет своего пользователя, или его ход закончился и он держит только фоновые задачи, runner выпускает его сразу же. Сеанс возобновляется, когда его пользователь отправляет свое следующее сообщение.
* Если ход все еще работает, runner ждет, пока ход закончится, или пока сеанс не будет ждать своего пользователя, и затем выпускает его.
* Если сеанс все еще на runner, когда окно благодати истекает, runner завершает его, и любая работа работающего хода потеряна. Ход, ожидающий одобрения, запрошенного изнутри работающего вызова инструмента, - это один способ, которым сеанс переживает окно.

Выпущенный сеанс возобновляется с свежего клона, поэтому работа, которую он не отправил, потеряна в любом случае; см. [Resumed sessions lose unpushed work](#additional-limitations). До v2.1.260, runner завершал каждый сеанс на лимите, после ожидания максимум окна благодати для работающего хода, чтобы закончиться.

Установите флаг выше вашего самого длинного ожидаемого сеанса, такой как `--kill-session-after-min 480` для 8 часов. Чтобы освободить слоты из разговоров, которые становятся неактивными, используйте вместо этого `--release-idle-session-min`.

<h3 id="additional-limitations">
  Дополнительные ограничения
</h3>

* **Возобновленные сеансы теряют неотправленную работу**: когда сеанс выпущен или его runner перезагружен, и пользователь отправляет другое сообщение, сеанс возобновляется на свежем runner, который клонирует репозиторий снова с его начальной ветки, поэтому работа, которую сеанс не отправил, потеряна. Установите [`--push-outcome-on-release`](/docs/ru/self-hosted-environments-reference#runner-cli-flags), чтобы runner сделал best-effort push веток результата сеанса перед выпуском, поэтому возобновленный сеанс начинается с этих коммитов вместо этого; это сохраняет подтвержденную работу, а не грязное рабочее дерево. Перед включением ограничьте, кто может push на `claude/*` refs на исходном удаленном, например с помощью набора правил ветки: при возобновлении runner получает ранее отправленную ветку без проверки, кто ее отправил, поэтому любой с доступом push на эти refs может поместить содержимое в возобновленное рабочее пространство. Runner также отбрасывает конфигурацию для каждого сеанса при возобновлении, означая каталог конфигурации Claude сеанса и любое состояние shell, которое сеанс написал; `--push-outcome-on-release` не охватывает это.
* **Приватные репозитории не могут быть добавлены в середине сеанса**: репозиторий, добавленный в сеанс после его запуска, не клонируется с учетными данными на самостоятельно размещаемом runner, поэтому добавление не удается. Выберите каждый репозиторий, который сеансу нужен, когда вы его создаете.
* **Некоторые коннекторы не появляются в самостоятельно размещаемых сеансах**: коннектор, который вы еще не подключили в параметрах claude.ai, не указан в самостоятельно размещаемом сеансе, и сеанс не будет вас приглашать подключить его. Подключите его в параметрах сначала, затем запустите свежий сеанс. Добавление коннектора в уже работающий сеанс тоже не делает его инструменты доступными для Claude; запустите свежий сеанс, чтобы подхватить недавно добавленный коннектор.

<h3 id="report-an-issue">
  Сообщите о проблеме
</h3>

Для проблем с самостоятельно размещаемыми окружениями свяжитесь с командой вашего аккаунта Anthropic.

<h2 id="troubleshooting">
  Устранение неполадок
</h2>

Для управляемой диагностики запустите подкоманду doctor на хосте runner. Подкоманда doctor запускает интерактивный сеанс Claude Code с логами и состоянием runner прикрепленными. Сначала войдите с `claude auth login` на этом хосте, чтобы сеанс мог запросить ваше окружение, его runners и его поставленные в очередь сеансы. Без этого входа, например, когда хост аутентифицируется с помощью ключа API, он ограничен локальной конечной точкой здоровья, метриками и логом runner, и он читает лог только если вы запустили runner с `--log-file`.

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

Распространенные проблемы:

* **Runner не появляется в окружении**: подтвердите, что хост может достичь `api.anthropic.com` через HTTPS, секрет окружения текущий и часы хоста находятся в пределах пяти минут от реального времени; большее смещение вызывает отказ аутентификации. Runner регистрирует `[runner:fatal]` с причиной отказа при отказе аутентификации.
* **Runner выходит при запуске с `cannot create or write to base directory`**: runner не может создать или писать в `--base-dir`, который по умолчанию `/workspace`. Исправьте владение каталога или укажите `--base-dir` на записываемый путь, как описано в [Keep the base directory and capacity identical across runners](#keep-the-base-directory-and-capacity-identical-across-runners). Если runner вместо этого регистрирует `[runner:fatal]`, говоря, что проверка базового каталога истекла по времени, каталог находится на зависшем монтировании NFS или CSI. Проверьте здоровье монтирования, а не разрешения. Runner печатает оба эти отказа при запуске в stderr перед открытием `--log-file`, поэтому ищите их в терминале или логах контейнера вашей платформы, а не в файле логов. До v2.1.225, runner не проверял базовый каталог при запуске, и эта неправильная конфигурация не удавалась сеансам после подхвата вместо этого.
* **Сеансы остаются поставленными в очередь**: каждый онлайн runner может быть заблокирован для другого владельца. Проверьте метрику `claude_code_self_hosted_runner_locked_account` каждого runner [metric](/docs/ru/self-hosted-environments-reference#prometheus-metrics) или поле `locked_account` его строки логов `[runner:health]`, чтобы увидеть, кто его держит. Оба показывают email владельца только после того, как runner получил токен сеанса, несущий претензию `act.email`, которую сеансы агента Claude Tag никогда не делают. Без претензии runner не излучает серию `locked_account` и регистрирует `locked_account=yes`, что говорит вам, что runner заблокирован, но не для какого владельца. Добавьте реплики или ждите, пока существующий runner осушится и перезагрузится. Если окружение использует on-demand runners, проверьте оркестратор вместо этого; см. [On-demand runners](/docs/ru/self-hosted-environments-configuration#on-demand-runners).
* **Сеансы не удаются сразу после подхвата**: откройте сеанс в claude.ai/code, чтобы увидеть ошибку. Наиболее распространенные причины - отсутствие [git credentials](#configure-git) в образе runner и инструменты сборки, которые не установлены. Неписываемый базовый каталог останавливает runner при запуске вместо того, чтобы не удавались сеансы. См. запись **Runner exits at startup with `cannot create or write to base directory`** в этом списке.
* **Сеансы не могут достичь сеть через аутентифицирующий исходящий прокси**: когда источник, который вы установили с помощью [`--proxy-authorization-command` или `--proxy-authorization-file`](#authenticate-to-an-egress-proxy), не удается, истекает по времени после 30 секунд или дает пустое значение, runner отвечает на это соединение `502 Bad Gateway` и регистрирует почему. Runner редактирует stderr команды в этом логе и никогда не регистрирует значение заголовка. С `--proxy-authorization-command`, запустите команду самостоятельно на хосте, чтобы подтвердить, что она печатает все значение заголовка на stdout. Если runner вместо этого выходит при запуске с `could not start the proxy-authorization listener`, он не мог открыть свой слушатель loopback.
* **Runner регистрирует строки `Poll failed`, содержащие `rejecting the malformed poll response`**: runner получил ответ на опрос работы, чье тело не является ожидаемым JSON очереди, чаще всего потому что что-то между runner и `api.anthropic.com`, такое как перехватывающий прокси или captive portal, ответил своей собственной страницей. Runner отклоняет ответ, считает его под видом `transport` метрики `claude_code_self_hosted_runner_poll_errors_total` [metric](/docs/ru/self-hosted-environments-reference#prometheus-metrics) и повторяет попытку по расписанию отказа опроса, описанному в [Session lifecycle](/docs/ru/self-hosted-environments#session-lifecycle). Runner продолжает обслуживать свои живые сеансы. Конфигурируйте прокси, чтобы пропустить ответы от `api.anthropic.com` без изменений. До v2.1.246, runner читал такой ответ как пустую очередь работы, которая могла закончить его живые сеансы или заставить его выйти.
* **Ветка сеанса больше не существует на удаленном**: для источника git, который сеанс только читает, runner пропускает этот источник и продолжает на оставшихся. Для источника, на который сеанс отправляет результаты, удаленная ветка, обычно потому что она была объединена и auto-deleted, не удается сеанс с ошибкой, называющей репозиторий и ветку и просящей вас восстановить ветку и повторить попытку. Runner не удается сеанс с той же ошибкой, когда пропуск оставил бы его без репозитория вообще. До v2.1.228, такой сеанс начинался в пустом каталоге.
* **Сеансы занимают минуты для запуска**: начальный клон обычно доминирует. Смотрите метрику `claude_code_self_hosted_runner_session_init_duration_seconds` [metric](/docs/ru/self-hosted-environments-reference#prometheus-metrics), чтобы подтвердить, и сократите клон с помощью [pre-warmed checkout](#reuse-a-pre-warmed-checkout) или меньшего `CLAUDE_RUNNER_FETCH_DEPTH`.
* **Pod убивается в середине осушения**: поднимите `terminationGracePeriodSeconds` по крайней мере на значение, которое runner регистрирует при запуске. См. [Shutdown timing](#shutdown-timing).

Как только логирование инициализируется, runner пишет свой лог жизненного цикла, включая строки `[runner:fatal]`, в stdout и отладочный вывод в stderr, все как простые текстовые строки, а не JSON. Отказы при запуске, описанные в записях устранения неполадок выше, печатаются в stderr перед этой точкой. Захватите оба потока с помощью `--log-file`, что также позволяет `self-hosted-runner doctor` их отслеживать, или с помощью сбора логов вашей платформы. Каждый процесс дочернего сеанса пишет отдельный отладочный лог. При отказе runner сохраняет лог, печатает путь логов в логе runner и выводит хвост логов рядом с сеансом в claude.ai/code.

<h2 id="what’s-next">
  Что дальше
</h2>

* [Customize sessions](/docs/ru/self-hosted-environments-configuration): скрипты-обертки, lifecycle hooks, on-demand runners, MCP servers и разрешения
* [Test end to end](/docs/ru/self-hosted-environments-testing): проверьте новый образ runner из CI перед продвижением
* [Reference](/docs/ru/self-hosted-environments-reference): каждый флаг CLI, переменная окружения и метрика
