Skip to main content
자체 호스팅 환경은 Team 및 Enterprise 플랜에서 공개 베타 상태이며, Owner가 Cloud environments 관리자 페이지에서 Allow self-hosted environments를 활성화합니다. 이 페이지는 작동하는 러너를 가정합니다. 설정은 빠른 시작을 참조하고 프로덕션에 배포는 플릿 레시피를 참조하십시오.
자체 호스팅 환경은 배포하는 러너 프로세스에 의해 실행되는 자신의 인프라에서 Claude Code 클라우드 세션을 실행합니다. 구성이 없으면 해당 러너는 세션의 저장소를 복제하고, Claude Code를 생성하며, 정리합니다. 이 페이지는 러너를 운영하는 플랫폼 엔지니어를 위한 것입니다. 세션별 자격 증명 프로비저닝에서 체크아웃 완전 교체까지 기본값이 맞지 않을 때의 확장 지점을 다룹니다. 래퍼와 훅은 Linux 또는 macOS인 러너 호스트에서 실행 가능한 파일로 실행되며, 이 페이지의 예제는 POSIX 셸을 가정합니다. 이 페이지의 일부 훅 환경 변수는 여전히 pool을 사용합니다(예: CLAUDE_RUNNER_POOL_ID). CLI 플래그 및 환경 변수 이름은 environment을 사용합니다(예: --environment-secret-file).

래퍼 스크립트

각 세션이 러너가 자체적으로 수행할 수 없는 설정이 필요할 때 래퍼 스크립트를 사용합니다. 세션 작성자로 범위가 지정된 단기 자격 증명 프로비저닝, 환경별 비밀 내보내기, 언어 도구 체인 준비, 또는 자식 프로세스 주변의 리소스 제한 적용입니다. 러너는 세션당 한 번 Claude Code 바이너리 대신 래퍼를 시작합니다. $CLAUDE_RUNNER_CLAUDE_BIN(러너 자체의 바이너리)으로 exec하여 래퍼를 종료하면 신호와 종료 코드가 올바르게 전파됩니다. 러너를 시작할 때 --exec-path 또는 SELF_HOSTED_RUNNER_EXEC_PATH를 래퍼로 지정합니다:
러너는 래퍼의 환경에 다음을 설정합니다: 래퍼는 또한 서버 제공 환경 변수를 포함한 자식의 관리되는 환경의 나머지를 상속합니다. exec는 모두 자동으로 전파합니다. 래퍼가 자식을 다른 방식으로 생성하면 전체 환경을 전달하세요. CLAUDE_CODE_REMOTE_SLACK_* 변수는 래퍼 또는 command 훅에서 읽을 수 있으며, 셸 명령, git 훅, Claude Code 훅 등 세션이 실행하는 항목에서도 읽을 수 있습니다. checkout, post-session, spawn-runner 훅은 이를 받지 않습니다.

설정되지 않을 수 있는 변수에 기본값 지정

스크립트가 set -u를 사용하는 경우 CCR_SESSION_ACCOUNT_EMAIL, CLAUDE_RUNNER_CLIENT_PLATFORM, CLAUDE_CODE_REMOTE_SLACK_THREAD_URL, CLAUDE_CODE_REMOTE_SLACK_THREAD_TS, CLAUDE_CODE_REMOTE_SLACK_CHANNEL_ID를 "${CCR_SESSION_ACCOUNT_EMAIL:-}"와 같이 기본값을 지정하여 확장하세요. 각 변수는 설정되지 않을 수 있으며, set -u에서는 설정되지 않은 변수를 확장하면 Bash가 unbound variable로 중지됩니다.

스크립트가 확장하는 인수와 변수를 따옴표로 묶기

러너가 래퍼 또는 command 훅에 전달하는 모든 인수와 변수를 따옴표로 묶으세요. 일부 값은 JSON, 완전한 문장, 또는 Bash(npm run test *)와 같은 권한 규칙을 담고 있어 셸이 해석하는 문자를 포함합니다.
  • 인수: "$@"로 그대로 전달하세요
  • 변수: "${CLAUDE_CODE_REMOTE_SLACK_THREAD_URL:-}"와 같이 각 변수를 따옴표로 묶으세요. 예를 들어 Slack 스레드 링크에는 ? 및 &가 포함될 수 있습니다.
  • eval 및 sh -c 문자열: 따옴표 안이라도 eval 또는 sh -c가 실행하는 문자열에 값을 치환하지 마세요. 대신 해당 문자열이 변수를 참조하도록 하세요.

stdin 및 파일 디스크립터 3을 연결된 상태로 유지

자식의 stdin은 러너의 제어 채널입니다. 토큰 회전 및 세션 종료 신호가 이를 통해 도착합니다. 러너는 또한 파일 디스크립터 3에서 파이프를 열고 자식의 활동 신호를 읽어 유휴 및 시작 타임아웃을 구동합니다. 일반 exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"는 둘 다 자동으로 보존합니다. 래퍼가 맨 &로 자식을 백그라운드에 두면 자식의 stdin이 끊깁니다. 세션은 초기 OAuth 토큰의 약 30분 수명이 만료될 때까지 정상으로 보이다가 해당 토큰을 사용하는 모든 API 호출이 401 authentication_error로 실패합니다. 래퍼가 자식을 백그라운드에 두어야 하는 경우(예: 정리 트랩을 살리기 위해) stdin을 파일 디스크립터 9에 저장하고 명시적으로 다시 연결하세요:
자식의 stdout은 리디렉션해도 됩니다. 파일 디스크립터 3과 stderr는 러너에 연결된 상태로 유지하세요:
  • 파일 디스크립터 3: 자식의 활동 신호를 러너에 전달합니다. 래퍼에서 이를 닫거나 재사용하지 마세요.
  • 파일 디스크립터 4 및 5: Claude Code v2.1.309 이상의 러너는 이를 자식용으로 예약합니다. 래퍼에서 이를 닫거나 재사용하지 마세요. 래퍼가 둘 중 하나를 사용하는 경우(예: stdin 저장) 해당 용도를 6에서 9 사이의 디스크립터로 옮기세요. 이 범위는 이전 버전에서도 비어 있습니다.
  • stderr: 래퍼 또는 자식이 0이 아닌 값으로 종료되면 러너는 stderr의 마지막 줄을 세션에 게시하고 자체 로그에 출력합니다. 세션 사용자가 해당 줄을 보게 되므로 stderr에 비밀을 출력하지 말고, 래퍼를 배포하기 전에 set -x를 제거하세요. stderr를 리디렉션해도 세션은 계속 실행되지만, 러너는 종료 코드만으로 실패를 보고합니다.

시스템 프롬프트 플래그 전달

Anthropic의 제어 평면이 세션에 보내는 시스템 프롬프트와 추가 시스템 프롬프트는 인라인 텍스트가 아닌 파일 경로로 래퍼에 전달됩니다. 러너는 각 프롬프트를 세션의 구성 디렉토리인 CLAUDE_CONFIG_DIR의 파일에 작성하고, 래퍼가 받는 인수에 --system-prompt-file <path> 또는 --append-system-prompt-file <path>로 해당 경로를 전달합니다. Claude Code v2.1.281 이상의 러너는 프롬프트를 파일로 전달합니다. v2.1.281 이전에는 러너가 이를 --system-prompt <text> 및 --append-system-prompt <text>로 전달했습니다. 래퍼 스크립트 또는 command 훅에서 이러한 플래그를 다음과 같이 처리하세요:
  • 그대로 전달: exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"로 래퍼를 종료하면 파일 플래그가 다른 모든 인수와 함께 전달됩니다. 이를 제거하거나 다시 작성하지 마세요. 세션에서 프롬프트 파일 플래그가 누락되면 제어 평면이 해당 세션에 보낸 지침 없이 실행됩니다.
  • v2.1.281 이상의 러너에서는 추가한 파일 플래그가 서버의 플래그에 더해지지 않고 이를 대체함: 각 프롬프트 파일 플래그는 단일 값을 받으며 Claude Code는 마지막 항목을 유지하므로, "$@" 뒤에 --append-system-prompt-file <path>를 추가하면 해당 파일의 내용이 서버의 추가 지침을 대체합니다. 서버의 지침 위에 지침을 추가하려면 러너 이미지의 CLAUDE.md에 넣으세요. 러너는 이를 모든 세션의 사용자 수준 구성에 시드합니다.

세션 시작 시 Argument list too long 해결

Linux에서 제어 평면의 프롬프트와 자체 텍스트를 하나의 --append-system-prompt 인수로 결합하면, 인수가 128 KB에 도달할 때 러너가 다음과 같은 줄을 로그에 기록합니다:
Linux는 각 단일 인수를 32개 메모리 페이지, 즉 일반적인 4 KB 페이지 기준 128 KB로 제한합니다. getconf ARG_MAX의 값(보통 2 MB)은 모든 인수와 환경을 합한 전체에 대한 별도의 제한입니다. Claude Code v2.1.283 이상에서는 파일 플래그를 "$@"에 그대로 두고 자체 텍스트만 --append-system-prompt로 전달하세요. Claude Code는 제어 평면의 프롬프트를 먼저, 그다음 자체 텍스트를 적용하여 둘 다 사용합니다:
Linux에서 자체 텍스트가 128 KB에 도달하면 여전히 하나의 인수이므로 러너 이미지의 CLAUDE.md로 옮기세요. v2.1.281 또는 v2.1.282에서는 해당 명령이 Cannot use both --append-system-prompt and --append-system-prompt-file로 종료됩니다. 이전 버전에서는 자체 텍스트가 오류 없이 제어 평면의 프롬프트를 대체합니다. 이 중 어느 버전에서든 먼저 업데이트하거나 자체 텍스트를 CLAUDE.md로 옮기세요.

세션 작성자로 범위가 지정된 자격 증명 프로비저닝

decode-token 서브명령을 사용하여 세션 JWT에서 클레임을 읽으세요. 인수, CLAUDE_CODE_SESSION_ACCESS_TOKEN 또는 stdin에서 토큰을 읽습니다(이 순서대로). 세션 내에서 토큰 확인을 참조하여 확인 내용을 확인하세요. 아래 예제는 작성자 ID를 디코딩하고, 단기 AWS 자격 증명으로 교환하고, Claude Code로 실행합니다:
추출된 클레임이 인증 결정을 제어할 때 jq -r 대신 jq -re를 사용하면 누락된 클레임이 리터럴 문자열 null을 다운스트림으로 전달하는 대신 0이 아닌 값으로 종료됩니다. 조직 서비스 ID(예: 봇 및 에이전트 세션)에 의해 생성된 세션은 user: 주체 대신 agent: 주체를 전달하므로 이 예제는 이를 거부합니다. 환경이 이러한 세션을 제공하는 경우 래퍼가 종료하는 대신 기본 자격 증명으로 폴백할지 명시적으로 결정하세요. 자격 증명 교환에 대신 이메일이 필요한 경우 .act.email을 읽고 부재를 처리하세요. 토큰은 작성 사용 환경이 기록한 경우에만 이를 전달하며 CLI 디스패치 세션에는 이메일이 없을 수 있습니다. 전체 클레임 참조 및 러너 외부 서비스의 검증은 세션 ID 확인을 참조하세요.

라이프사이클 훅

라이프사이클 훅은 러너의 세션별 파이프라인 단계를 사용자 자신의 스크립트로 대체합니다. --hooks-dir <path> 또는 SELF_HOSTED_RUNNER_HOOKS_DIR로 러너가 훅 디렉터리를 가리키도록 지정합니다. 러너는 잘 알려진 이름을 가진 실행 파일을 찾으며, 존재하지 않는 훅은 기본 제공 동작으로 넘어가므로 필요한 훅만 작성하면 됩니다. 훅은 러너 자체의 권한으로 실행되고 세션 자식 프로세스도 같은 UID를 공유하므로, 세션 코드가 훅 디렉터리를 수정할 수 없도록 읽기 전용으로 마운트하거나 이미지에 포함시키십시오. 강화 섹션을 참조하십시오. 이 훅은 세션 내부에서 실행되는 Claude Code 훅과 다릅니다. 라이프사이클 훅은 세션 바깥의 러너에서 세션 전후로 실행됩니다.

checkout

저장소마다 한 번씩, 러너의 기본 제공 clone 및 fetch 대신 실행됩니다. 이 훅을 사용하여 HTTPS 또는 SSH로 접근하는 read-through 미러에서 clone하거나, 아카이브에서 작업 트리를 준비하거나, 세션별 git 인증을 적용할 수 있습니다. 러너는 다음 변수를 설정하며, 표에 나열되지 않은 다른 CLAUDE_RUNNER_ 변수를 설정할 수도 있습니다. 스크립트는 CLAUDE_RUNNER_CHECKOUT_PATH에 요청된 리비전으로 체크아웃된 작업 트리를 남겨 두어야 합니다. 러너가 그 위에 세션의 작업 브랜치를 생성하므로 detached HEAD도 동작합니다. 훅이 반환된 후 러너는 CLAUDE_RUNNER_CHECKOUT_PATH에 .git이 포함되어 있는지 확인합니다. 훅이 Perforce나 압축 해제된 tarball과 같은 git이 아닌 소스를 구성하는 경우, 러너 환경에 CLAUDE_RUNNER_SKIP_GIT_VERIFY=1을 설정하여 이 검사를 건너뛰십시오. 작업 브랜치 생성이나 결과 push와 같은 git 기반 흐름은 git 체크아웃이 필요하므로, git이 아닌 트리의 결과물은 post-session 훅으로 내보내십시오.

훅에서 git 자격 증명 얻기

러너는 훅에 git 자격 증명을 전달하지 않습니다. checkout 훅 환경에는 CLAUDE_RUNNER_CLAUDE_BIN이 설정되지 않으므로 decode-token 하위 명령도 여기서는 사용할 수 없습니다. 대신 세션의 ID로부터 세션별 clone 자격 증명을 발급하거나, 호스트 자체의 git 인증으로 대체하십시오.
  • 세션별 clone 자격 증명: 서비스에서 토큰 검증하기에 설명된 대로, 표준 JWT 라이브러리를 사용하여 CLAUDE_RUNNER_API_BASE_URL 아래의 JWKS 엔드포인트에 대해 CLAUDE_CODE_SESSION_ACCESS_TOKEN을 검증합니다. 그런 다음 자격 증명 서비스가 토큰의 act 클레임에 있는 ID에 대해 수명이 짧은 clone 자격 증명을 발급하도록 합니다. 해당 자격 증명은 act.sub를 키로 사용하고, act.email을 요구하지 마십시오.
  • 호스트 git 인증: SSH 에이전트, 자격 증명 헬퍼 또는 .netrc와 같이 호스트에 이미 있는 git 인증을 사용합니다.

훅이 실패하는 경우

훅이 0이 아닌 코드로 종료되거나, 0으로 종료되었지만 사용 가능한 체크아웃을 남기지 않으면 훅이 실패한 것입니다.
  • 세션이 결과를 push하는 저장소: 러너는 세션을 실패 처리하며, 0이 아닌 코드로 종료된 경우 스크립트 stderr의 마지막 부분을 사용자에게 표시합니다. 실패 이유를 짧은 한 줄로 stderr에 마지막으로 기록한 다음 0이 아닌 코드로 종료하십시오. stderr에 비밀 정보를 출력하지 말고, 훅을 배포하기 전에 set -x를 제거하십시오. 훅 stderr의 마지막 약 4 KB 안에 git의 couldn't find remote ref가 나타나면 러너는 대신 브랜치가 삭제되었다고 보고합니다. 그러면 세션의 오류 메시지는 해당 브랜치가 원격에 더 이상 존재하지 않는다고 알리고 사용자에게 브랜치를 복원하도록 요청합니다. 해당 브랜치가 존재한다면 세션이 no longer exists on the remote 또는 couldn't find remote ref로 실패하는 경우를 참조하십시오.
  • 세션이 읽기만 하는 저장소(예: 실행 중인 세션에 추가된 저장소): 러너는 해당 저장소를 건너뛰고 나머지 저장소로 계속 진행합니다. 러너는 세션에 Skipped 단계를 게시하고 훅이 체크아웃 경로에 남긴 내용을 제거합니다. Skipped 단계에 checkout 훅이 언급되어 있으면 러너 로그의 [runner:warn] checkout hook failed 줄에서 실패 세부 정보를 확인하십시오. 단계에 소스 ref가 no longer exists on remote라고 표시되면 세션이 no longer exists on the remote 또는 couldn't find remote ref로 실패하는 경우를 참조하십시오. 건너뛴 결과 세션에 저장소가 하나도 남지 않으면 러너는 결국 세션을 실패 처리합니다.
훅이 성공하면 러너는 세션이 끝난 후 체크아웃 경로를 제거합니다.

post-session

세션마다 한 번씩, Claude Code 자식 프로세스가 종료된 후 러너가 워크스페이스를 해체하기 전에 실행됩니다. 이 훅은 커밋되지 않은 작업을 저장할 수 있는 유일한 기회입니다. --capacity가 1보다 크면 러너는 훅이 반환된 직후 세션별 워크트리를 삭제하고, --capacity 1에서는 재사용되는 표준 clone이 다음 세션 시작 시 hard reset되므로, 어느 경우에도 커밋되지 않은 추적 변경 사항은 남지 않습니다. 일반적인 용도는 커밋되지 않은 변경 사항의 스냅샷 브랜치를 push하거나, 로그를 보관하거나, 자체 시스템에 세션 종료 이벤트를 보내는 것입니다. 이 훅은 자식 프로세스가 생성된 모든 세션 종료 시점에 원인과 관계없이 실행됩니다. 아래의 CLAUDE_RUNNER_EXIT_REASON 값이 각 경우를 나열합니다. VM 선점이나 전원 손실처럼 러너가 갑자기 종료되면 훅이 실행될 수 없습니다. 갑작스러운 종료에 대한 보장이 필요하다면 대신 Claude Code PostToolUse 훅을 사용하여 세션 내부에서 주기적으로 스냅샷을 만드십시오. 러너는 다음을 설정합니다. CLAUDE_RUNNER_EXIT_REASON은 다음 네 가지 값 중 하나를 가집니다.
  • completed: 세션이 정상적으로 종료되었습니다. Claude Code 프로세스가 정상 종료되었거나, 세션이 보관 또는 삭제된 후 스스로 종료되었습니다.
  • failed: Claude Code 프로세스가 충돌했거나, 프로세스 시작 후 설정이 실패했습니다.
  • interrupted: 러너가 다음 중 한 가지 경우로 세션을 중지했습니다.
    • 러너가 슬롯을 비우기 위해 세션을 해제했습니다.
    • 세션이 시작 시 시간 초과되었습니다.
    • 서버가 세션을 이 러너에서 다른 곳으로 옮겼습니다.
    • 프로세스가 종료되기 전에 러너의 폴링이 보관 또는 삭제를 감지했습니다.
    • 러너가 드레이닝 중이었습니다.
    • 세션이 --kill-session-after-min 제한을 초과했습니다.
  • abandoned: 다른 러너가 가져간(claim한) 세션을 위해 예약된 값입니다. 현재 해당 경우에는 훅이 실행되지 않습니다.
훅 수신 기록을 세션 라이프사이클 카운터와 비교하면, 일부 interrupted 기록이 카운터에서는 completed로 집계될 수 있습니다. 카운터는 해제, 시작 시 시간 초과, 서버 이동, 그리고 러너의 폴링이 먼저 감지한 보관 또는 삭제를 completed로 집계하는데, 이는 러너가 슬롯을 정상적으로 반환했기 때문입니다. 훅의 종료 상태는 세션 결과에 영향을 주지 않으며, 실패는 로그에 기록되고 무시됩니다. 러너는 러너 종료를 포함한 모든 세션 종료 시 --post-session-hook-timeout-sec(기본값 60초)까지 기다립니다. 다음 예시는 커밋되지 않은 작업을 구조용 브랜치에 저장합니다.
스크립트의 GIT_ALLOW_PROTOCOL 줄은 git을 HTTPS, HTTP, SSH 원격으로 제한합니다. 러너 환경에 이미 비어 있지 않은 자체 GIT_ALLOW_PROTOCOL 목록이 설정되어 있으면 스크립트는 해당 목록을 유지합니다. 훅은 러너 호스트에서 자체 환경에 있는 git 자격 증명으로 push합니다. 기본 제공 clone이 Anthropic git 프록시를 거치는 경우를 포함하여 이미지에 자격 증명을 두지 않는 방식에서는 자격 증명이 없으므로, push하기 전에 훅 안에서 수명이 짧은 push 자격 증명을 발급하십시오. 훅이 CLAUDE_CODE_SESSION_ACCESS_TOKEN으로 받는 세션 토큰을 자체 토큰 서비스와 교환하고, 세션 ID 검증하기에 설명된 대로 검증하십시오. 훅이 git에 제공하는 모든 자격 증명은 세션이 얻을 수 있는 것으로 간주하고, 이 push 이상의 작업은 할 수 없도록 발급하십시오. 훅 안의 git은 세션이 쓸 수 있는 설정 파일을 읽으며, 그 파일 중 하나에 지정된 자격 증명 헬퍼나 필터 드라이버는 훅의 권한으로 실행됩니다. 또한 그 파일의 설정은 어떤 원격을 지정하든 push 대상을 바꿀 수 있습니다. 러너가 훅에서 고정하는 git 설정과 해당 파일에 맡기는 설정은 라이프사이클 훅 내부의 git 구성을 참조하십시오.

러너가 세션을 해제할 때의 훅 실행 시점

해제된 세션은 다른 러너에서 재개될 수 있습니다. v2.1.236 이상의 러너에서는 해제 시점에 세션이 하던 작업에 따라 이 훅이 끝나기 전에 세션이 재개될 수 있는지가 결정됩니다.
  • 턴 이후 유휴 상태이거나 시작 시 시간 초과된 경우: 러너는 자식 프로세스를 중지하고 이 훅을 끝까지 실행합니다. 그 후에야 세션을 해제합니다. 훅이 실행되는 동안 보낸 사용자 메시지는 훅이 끝나기 전에 다른 러너에서 세션을 재개할 수 없습니다.
  • 권한 프롬프트와 같은 프롬프트에 사용자가 응답하기를 기다리는 경우: 러너는 먼저 세션을 해제한 다음 이 훅을 실행합니다. 훅이 실행되는 동안 보낸 사용자 메시지는 훅이 끝나기 전에 다른 러너에서 세션을 재개할 수 있습니다.
이는 러너가 세션을 해제할 때마다 적용됩니다. 유휴 타임아웃 시, --retire-at 시간에, 그리고 v2.1.260 이상의 러너에서는 세션의 --kill-session-after-min 제한에 도달했을 때 적용됩니다. 턴이 끝났고 백그라운드 작업만 보유한 세션은 여기서 유휴 상태로 간주됩니다. v2.1.236 이전에는 두 경우 모두 러너가 먼저 세션을 해제한 다음 이 훅을 실행했습니다. SIGTERM 드레이닝 중에는 러너가 훅이 끝날 때까지 세션 임대를 유지합니다. 종료 시점을 참조하십시오.

라이프사이클 훅 내부의 git 구성

checkout 및 post-session 훅은 환경에 세션의 액세스 토큰을 가진 채 실행되며, 이 훅이 실행하는 git은 ~/.gitconfig나 체크아웃의 .git/config처럼 세션이 쓸 수 있는 설정 파일을 읽습니다. 두 훅 중 하나가 실행되기 전에 러너는 아래 설정을 포함한 git 설정을 GIT_CONFIG_COUNT/GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n 쌍과 git 환경 변수로 훅 환경에 설정합니다. git은 이를 모든 설정 파일보다 높은 순위로 취급하며, 이 설정은 세션 자체의 git이 아니라 훅이 실행하는 git에만 적용됩니다. 시작 시 러너는 적용 중인 hooks 경로, 허용된 프로토콜, gpg 프로그램, 서명 모드를 보여 주는 [runner:git] lifecycle hooks: 줄을 출력합니다. Claude Code v2.1.280 이상이 필요합니다.
  • Git 훅: 사용자가 값을 제공하지 않으면 core.hooksPath는 /dev/null이므로, git은 저장소의 .git/hooks에 있는 훅과 ~/.gitconfig가 지정하는 모든 hooks 디렉터리를 건너뜁니다. 값을 제공하려면 러너 환경에서 core.hooksPath를 GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n 쌍으로 export하십시오. 러너는 시스템 git 구성에서도 core.hooksPath를 읽지만, 러너의 사용자가 해당 파일, 그 파일이 지정하는 디렉터리, 또는 그 안의 훅 파일에 쓸 수 없는 경우에만 사용합니다. 러너가 값을 무시하면 시작 시 [runner:warn] 줄에 해당 값과 이유가 표시됩니다.
  • 파일 시스템 모니터: core.fsmonitor가 비어 있으므로, 훅 안의 git은 설정 파일이 지정한 모니터 프로그램을 실행하지 않습니다.
  • 원격 프로토콜: GIT_ALLOW_PROTOCOL은 https:http:ssh입니다. 로컬 경로, file:// URL 또는 git:// URL을 사용하는 clone, fetch, push는 fatal: transport 'file' not allowed 또는 fatal: transport 'git' not allowed로 실패합니다.
  • SSH 명령 및 자격 증명 프롬프트: 훅 안의 git은 설정 파일의 core.sshCommand와 core.askPass를 무시합니다. 자체 SSH 명령을 사용하려면 러너 환경에 GIT_SSH_COMMAND를 설정하십시오. 자격 증명 프롬프트 프로그램을 사용하려면 같은 곳에 GIT_ASKPASS를 설정하십시오. 세션은 러너의 환경을 상속하므로 두 변수 모두 세션 자체의 git에도 전달됩니다. 둘 중 어느 것에도 자격 증명을 넣지 마십시오.
  • gpg 프로그램: gpg.program, gpg.openpgp.program, gpg.x509.program, gpg.ssh.program은 러너가 설정하는 경로이며, 설정 파일의 값은 절대 사용되지 않습니다.
  • 커밋 서명: --configure-git을 사용하면 훅에서 만든 커밋은 세션으로서 서명됩니다. 이 플래그가 없으면 commit.gpgsign과 tag.gpgsign은 false입니다.
이러한 설정 중 하나를 변경하려면 러너 환경이나 훅 안의 git -c 옵션을 사용하십시오.
  • 구성 쌍: 러너 환경에서 export한 GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n 쌍은 같은 키에 대한 러너의 값을 대체합니다. 쌍의 번호는 0부터 매기고 GIT_CONFIG_COUNT를 쌍의 개수로 설정하십시오. count가 알리는 마지막 쌍이 없으면 러너는 사용자의 모든 쌍을 무시하고 시작 시 [runner:warn] 줄을 로그에 기록합니다.
  • Git 환경 변수: 러너는 GIT_ALLOW_PROTOCOL, GIT_SSH_COMMAND, GIT_ASKPASS를 러너 환경에 설정한 그대로 둡니다.
  • git -c 옵션: 훅 안의 git -c 옵션은 러너의 것이든 사용자의 것이든 GIT_CONFIG_KEY_n 쌍을 재정의합니다. 그러나 git이 모든 구성보다 먼저 읽는 GIT_ALLOW_PROTOCOL, GIT_SSH_COMMAND, GIT_ASKPASS는 변경하지 않습니다.
훅 안의 git은 자격 증명 헬퍼, url.*.insteadOf 재작성, 필터 드라이버처럼 러너가 설정하지 않는 모든 설정을 세션이 쓸 수 있는 파일을 포함한 모든 설정 파일에서 여전히 읽습니다. 그러한 파일 중 하나에 지정된 자격 증명 헬퍼나 필터 드라이버는 훅의 권한을 가진 프로그램으로 실행되며, 그 파일의 구성은 명령줄에 전달한 URL로의 push를 포함하여 훅에서 수행하는 push의 대상을 여전히 바꿀 수 있습니다. v2.1.280 이전에는 러너가 이러한 설정을 전혀 설정하지 않았으며, --configure-git 환경에서는 훅이 -c commit.gpgsign=false를 전달하지 않으면 훅에서 만든 커밋이 실패했습니다.

command

세션마다 한 번씩 checkout 후에 기본 제공 자식 프로세스 생성 대신 실행됩니다. 이 훅은 래퍼 스크립트와 동일한 환경을 받으며, 같은 방식으로 "$CLAUDE_RUNNER_CLAUDE_BIN"으로 exec해야 합니다. 모든 사용자 정의를 하나의 hooks 디렉터리에 유지하려면 command 훅을 사용하고, 래퍼가 다른 곳에 있다면 --exec-path를 사용하십시오. --exec-path도 설정되어 있으면 플래그가 우선하며 command 훅은 무시됩니다. 항상 PATH에서 찾은 claude가 아니라 러너 자체의 바이너리를 exec하십시오. 그렇지 않으면 버전 고정이 무력화됩니다.

온디맨드 러너

고정 플릿을 실행하는 대신 세션당 하나의 러너를 부팅할 수 있습니다. 오케스트레이터는 별도의 상태 비저장 서브커맨드로, Anthropic에 스폰 요청을 폴링하며, 사용 가능한 러너가 없는 상태로 대기 중인 세션당 하나씩 폴링하고, 각각에 대해 spawn-runner 훅을 실행합니다. 훅은 워크로드를 플랫폼에 제출합니다: Kubernetes Job, EC2 인스턴스, Nomad dispatch. 온디맨드 러너는 자격증명 위생을 개선합니다. 고정 플릿에서는 환경 시크릿이 모든 러너 호스트에 존재하며, 이는 사용자 세션을 실행하는 동일한 호스트입니다. 오케스트레이터를 사용하면 환경 시크릿은 오케스트레이터 호스트에만 유지되며, 이 호스트는 사용자 코드를 실행하지 않습니다. 각 스폰된 러너는 정확히 하나의 러너를 등록한 후 만료되는 일회용 작업 지시서를 받습니다. 오케스트레이터를 시작하려면 환경 시크릿과 실행 가능한 spawn-runner 스크립트를 포함하는 훅 디렉토리를 전달합니다:
오케스트레이터는 폴 간에 상태를 유지하지 않으므로 가용성을 위해 동일한 환경에 대해 두 개 이상의 복제본을 실행할 수 있습니다. 각 스폰 요청은 서버 측에서 정확히 하나의 복제본에 의해 클레임됩니다. 모든 복제본은 동일한 --expected-spawn-seconds 값을 사용해야 합니다. 훅 계약을 참조하십시오.

spawn-runner 훅

오케스트레이터는 스폰 요청당 한 번씩 ${hooks-dir}/spawn-runner를 실행합니다. 훅은 러너 부팅을 기다리지 않고 비동기적으로 작업을 제출해야 하며, --hook-timeout 내에 반환해야 합니다. 기본값은 60초입니다. 훅은 다음을 받습니다: 스폰된 러너는 환경 시크릿 대신 작업 지시서로 등록합니다:
  • 작업 지시서로 시작합니다: --environment-secret-file을 작업 지시서 JWT를 포함하는 파일을 가리키도록 설정하거나, SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET을 JWT 값으로 설정합니다.
  • 훅이 종료되기 전에 JWT를 복사합니다: 오케스트레이터는 훅 종료 후 작업 지시서 파일을 삭제하므로, JWT를 제출하는 워크로드(예: 스폰된 Job의 Kubernetes Secret)에 복사하고, 파일 경로를 전달하지 마십시오.
  • 스폰된 러너에서 --capacity 1을 사용합니다: 세션 바운드 작업 지시서는 정확히 하나의 러너를 해당 세션에 바운드하여 등록하므로, 더 높은 용량은 작업을 받지 않는 슬롯을 추가하며, 러너는 시작 시 경고를 로깅합니다.
  • 사전 워밍 작업 지시서는 언바운드로 등록합니다: 대기 중인 러너는 세션에 바운드되지 않으며 고정 플릿 러너처럼 대기 중인 작업을 클레임합니다.
계약에는 네 가지 프로비저너 불가지론적 규칙이 있습니다:
  1. CLAUDE_RUNNER_ORDER_ID에서 멱등성을 유지합니다. 동일한 요청의 재전달은 최대 하나의 러너를 스폰해야 합니다. 주문 ID에서 결정론적 리소스 이름을 파생시키고 플랫폼이 중복을 거부하도록 합니다. 대신 CLAUDE_RUNNER_SESSION_ID를 키로 사용하지 마십시오. 세션의 모든 재요청은 새 주문 ID와 함께 동일한 세션 ID를 전달합니다. 세션 ID로 이름을 짓거나 중복 제거한 워크로드는 첫 번째 워크로드가 존재하는 동안 다시 생성되지 않습니다.
  2. 워크로드를 재시도하지 마십시오. 하나의 주문 ID는 최대 하나의 생성된 워크로드를 의미합니다. 러너가 등록되지 않으면 Anthropic은 --expected-spawn-seconds 후 새 주문 ID로 재요청합니다.
  3. 종료 코드 계약을 사용합니다. 결과에 맞는 상태로 종료합니다:
    • 종료 0: 제출됨.
    • 종료 1, 또는 --hook-timeout이 끝날 때 여전히 실행 중: 재시도 가능한 실패. 세션은 백오프되고 다시 제공됩니다. 연속으로 10번 요청이 실패하면 종료 2 이상일 때와 마찬가지로 차단됩니다.
    • 종료 2 이상: 재시도 불가능한 실패. 사용자가 세션에 새 메시지를 보내거나 Owner가 환경의 Activity 탭에서 해당 세션의 Retry를 선택할 때까지 세션은 다시 스폰되지 않습니다.
    세션이 백오프되는 동안에는 Activity 탭에 스폰 오류 없이 Queued로 표시되므로, 오케스트레이터의 로그에서 훅의 출력을 확인하십시오. 차단되면 세션은 Failed로 표시됩니다. 세션을 확장하면 훅 stderr의 마지막 512자에서 가져온 스폰 오류를 확인할 수 있습니다. stderr에는 조치 가능한 오류를 작성하고 시크릿은 작성하지 마십시오. 셸 훅에서는 일시적인 실패를 재시도 가능한 상태로 유지하십시오. 사전 워밍 요청의 경우 실패할 세션이 없습니다: 오케스트레이터는 0이 아닌 종료를 로컬에만 로깅하고, 서버는 --expected-spawn-seconds 리스가 만료된 후 스폰을 재요청합니다.
  4. --expected-spawn-seconds를 최소한 스폰 요청부터 러너 등록까지의 p99 시간으로 설정합니다. 오케스트레이터가 스폰 요청을 받은 시점부터 측정하고, 부팅 시간뿐 아니라 플랫폼에서 용량을 기다리는 시간도 포함하십시오. 이 값은 서버 측 리스이며 작업 지시서도 함께 만료되므로, 워크로드가 이보다 오래 걸리는 러너는 등록할 수 없습니다. 모든 오케스트레이터 복제본은 동일한 값을 사용해야 합니다.
훅이 stdout 또는 stderr에 작성하는 모든 것은 자격증명이 자동으로 삭제된 오케스트레이터의 로그에 나타납니다. 세션이 대기 중인 상태로 유지되면 오케스트레이터의 /healthz 본문에서 대기열 수를 확인한 후 Cloud environments 관리 페이지에서 환경의 Activity 탭을 엽니다: 실패한 세션을 확장하여 스폰 오류를 확인하고 Retry를 선택하여 재요청합니다.

다시 연 세션이 Queued 상태로 유지되거나 Failed로 표시되는 문제 해결하기

다시 연 세션이 Queued 상태로 유지되거나 Failed로 표시되거나 해당 세션에서 훅이 시간 초과된다면, 한 가지 원인은 세션 ID로 워크로드의 이름을 짓거나 중복 제거하는 훅입니다. 세션은 러너가 종료된 후 사용자가 해당 세션에 메시지를 보낼 때 다시 열립니다. 예를 들어 다음과 같이 각 워크로드의 이름을 만드는 훅을 살펴보겠습니다:
  1. 세션의 첫 번째 스폰 요청에서 훅은 해당 이름으로 워크로드를 생성하고 러너가 시작됩니다
  2. 러너가 종료되고(예: --release-idle-session-min 이후), 나중에 사용자가 세션을 다시 엽니다
  3. 세션의 다음 스폰 요청은 동일한 세션 ID와 새 주문 ID를 전달하므로 훅은 같은 이름을 만듭니다
  4. 러너가 종료될 때 아무것도 첫 번째 요청의 워크로드를 제거하지 않아 플랫폼에 해당 워크로드가 여전히 남아 있다면, 훅은 새 워크로드를 생성하지 않고 러너도 시작되지 않습니다
  5. 이후 해당 세션의 모든 스폰 요청이 같은 방식으로 끝납니다
직접 작성한 훅을 열어 워크로드가 이미 존재할 때(예: 플랫폼이 중복된 이름을 거부할 때) 어떻게 동작하는지 확인하십시오. 첫 번째 열에서 해당 동작을 찾으면 다시 연 세션이 Activity 탭에 어떻게 표시되는지 알 수 있습니다: 마지막 행의 스폰 오류에서 60000은 밀리초 단위의 --hook-timeout 값입니다. 훅이 --hook-timeout이 끝날 때까지 대기하면 오케스트레이터의 로그에도 timed out after 줄이 기록됩니다. 훅을 수정하려면 훅 계약의 규칙 1에 따라 각 이름이나 키를 CLAUDE_RUNNER_ORDER_ID로 만드십시오. 예시에서 이름은 다음과 같이 바뀝니다:
훅이 대기한다면, 대기하는 훅은 오케스트레이터의 다음 훅을 지연시키므로 워크로드를 제출한 후 종료하도록 함께 수정하십시오. 훅을 변경한 후 Activity 탭에서 멈춰 있는 각 세션의 상태를 확인하십시오:
  • Queued: 세션이 자동으로 다음 스폰 요청을 받습니다
  • Failed: Owner로서 해당 세션의 Retry를 선택하거나, 세션의 사용자가 새 메시지를 보내도록 하십시오

유휴 상태일 때 온디맨드 러너 종료하기

기본적으로 훅이 시작한 러너는 세션의 턴이 끝나도 종료되지 않으므로, 제출한 워크로드가 계속 실행됩니다. 러너가 종료되도록 하려면 훅이 다음 러너 플래그를 전달하도록 하십시오: 러너가 종료되면 워크로드를 제거하십시오. 예를 들어 Kubernetes Job에 ttlSecondsAfterFinished를 설정하거나, VM의 시작 스크립트가 종료 후 VM을 삭제하도록 합니다.

백그라운드 프로세스의 출력을 파일로 보내기

spawn-runner 훅이 백그라운드에서 실행 중인 프로세스를 남겨 두는 경우, 해당 프로세스의 출력을 훅의 사용자가 생성할 수 있는 파일로 보내십시오. 예를 들어 프로세스를 시작하는 명령에 >> "$HOME/claude-runner.log" 2>&1를 &보다 앞에 추가합니다. 다음 훅 줄은 리디렉션이 들어가는 위치를 보여 주며, your-background-command는 훅이 백그라운드로 실행하는 명령을 나타냅니다:
리디렉션이 없으면 프로세스는 훅의 stdout과 stderr에 씁니다:
  • 훅이 종료되고 약 1초 후 오케스트레이터는 exited, but a process it started still has the hook's stdout or stderr open을 로그에 기록합니다. 그 줄 이후로는 해당 출력을 [runner:hook:spawn-runner:after-exit jti=<order id>] 아래에 최대 64KB까지 더 기록한 다음 나머지는 버립니다.
  • 오케스트레이터가 종료되면 프로세스가 그곳에 쓰는 작업은 실패합니다.
훅이 직접 시작하는 러너는 환경 시크릿을 보유한 오케스트레이터 호스트에서 세션을 실행합니다. v2.1.305 이전에는 훅이 시작한 프로세스가 훅의 stdout 또는 stderr를 열어 두고 있으면 이미 종료된 훅도 --hook-timeout에 도달했으며, 다음과 같이 표시되었습니다:
  • Activity 탭, 실패한 세션의 스폰 오류: spawn-runner hook timed out after 60000ms 또는 훅 stderr의 마지막 줄들.
  • 오케스트레이터 로그: timed out after 60000ms, sending SIGTERM to process tree.

러너를 기다리지 않고 훅 종료하기

모든 온디맨드 러너가 훅 시작 후 약 60초 뒤에 중지되고 동일한 세션에 대해 훅이 다시 실행된다면, 훅이 러너를 기다리는지 확인하십시오. 60초는 기본 --hook-timeout이므로, 직접 값을 설정했다면 러너는 훅이 시작된 후 그 시간이 지나면 중지됩니다. 오케스트레이터 호스트에서 claude self-hosted-runner를 시작하고 포그라운드에서 또는 wait로 이를 기다리는 훅은 --hook-timeout이 끝날 때에도 여전히 실행 중입니다. 그러면 오케스트레이터는 훅과 그 아래에서 실행 중인 프로세스(해당 러너와 세션의 Claude 프로세스 포함)를 중지하므로, 아직 실행 중인 턴이 중단됩니다.
  • 타임아웃이 러너를 중지시키는 원인인지 확인하려면: 오케스트레이터의 로그에서 모든 스폰 요청마다 timed out after 60000ms, sending SIGTERM to process tree가 있는지 확인하십시오. 60000은 밀리초 단위의 --hook-timeout 값입니다.
  • 해결하려면: 훅이 러너를 플랫폼에 제출하고 0으로 종료하도록 하십시오. 대신 --hook-timeout을 늘리면 러너가 더 나중에 중지될 뿐입니다.

셸 훅에서 일시적인 실패를 재시도 가능한 상태로 유지하기

set -e를 사용하는 셸 훅에서는 재시도로 해결될 수 있었던 실패가 세션을 차단할 수 있습니다. 훅은 실패한 명령에서 멈추고 해당 명령 자체의 상태로 종료하며, 오케스트레이터는 그 상태에 종료 코드 계약을 적용합니다. 명령이 설치되지 않았을 때의 127이나 HTTP 오류 시 curl --fail이 반환하는 22처럼 많은 실패가 2 이상의 상태를 반환하므로, 첫 번째 실패에서 세션이 차단됩니다. 훅이 이미 차단한 세션은 사용자가 새 메시지를 보내거나 Owner가 환경의 Activity 탭에서 해당 세션의 Retry를 선택할 때까지 차단된 상태로 유지됩니다. 이러한 실패를 대신 종료 1로 바꾸려면 훅의 #! 줄 바로 아래, 실패할 수 있는 어떤 코드보다도 위에 다음 줄을 넣으십시오:
이 줄들은 훅의 나머지 부분이 동작하는 방식을 바꾸므로, 추가한 후 다음 각 패턴이 있는지 훅을 확인하십시오:
  • 단독 exit 2 이상: trap이 설정되면 종료 1이 됩니다. 어떤 재시도로도 해결할 수 없는 오류의 경우 대신 permanent "namespace claude-runners does not exist"처럼 이유와 함께 permanent를 호출하십시오. $( ), ( ) 또는 파이프 내부가 아닌 메인 셸에서 호출하십시오.
  • exec: exec는 셸을 대체하여 trap이 실행되지 않으므로, 훅의 마지막 명령을 exec로 시작하지 마십시오.
  • 두 번째 EXIT trap: 두 번째 trap ... EXIT는 첫 번째를 대체하므로 두 trap을 하나로 병합하십시오. 정리 명령을 rc=$?; 바로 뒤에 넣고 각각을 || true;로 끝내십시오. 그러면 정리 작업이 성공 시뿐 아니라 실패 시에도 실행되며, 정리 명령이 실패해도 훅의 종료 상태가 설정되지 않습니다. 다음 병합된 trap은 그 형태를 보여 주며, your-cleanup-command는 사용자 자신의 명령을 나타냅니다:
  • 실패해도 되는 명령: 훅이 이전에 set -e를 사용하지 않았다면, 이제 아무것도 찾지 못한 조회처럼 0이 아닌 값을 반환하는 첫 번째 명령에서 멈춥니다. 훅이 결과에 따라 동작한다면 해당 명령을 if의 조건으로 만드십시오. 결과를 무시한다면 명령 뒤에 || true를 붙이십시오.
  • 워크로드를 제출하는 명령: 이 명령 뒤에는 || true를 붙이지 마십시오. || true를 붙이면 할당량 초과 등으로 플랫폼이 제출을 거부하더라도 훅이 0으로 종료하며, 세션은 시작되지 않은 러너를 계속 기다리게 됩니다. 훅의 첫 번째 규칙에 따라 워크로드 이름을 CLAUDE_RUNNER_ORDER_ID를 기준으로 지정하십시오. 그러면 이미 존재한다는 응답은 동일한 요청의 재전달이므로, 그 응답만 성공으로 처리하십시오. 그 밖의 응답은 stderr에 출력하고 종료 1로 종료하거나, 어떤 재시도로도 해결할 수 없다면 permanent를 호출하십시오. 다음 예시는 워크로드만 담은 job.yaml을 사용하여 kubectl create에 대해 이를 수행합니다:
    그러면 오케스트레이터의 로그에 해당 메시지가 표시됩니다. 훅이 다른 객체도 생성한다면 각 객체의 이름을 주문 ID를 기준으로 지정하고, 각각에 별도의 명령을 사용하며, 이미 존재한다는 응답을 같은 방식으로 성공으로 처리하십시오. 시크릿을 담는 객체의 경우 명령의 출력 대신 고정된 메시지를 출력하십시오.
trap이 작동하는지 확인하려면 trap 줄 바로 아래에 no-such-command처럼 존재하지 않는 명령을 호출하는 줄을 추가하십시오. 셸에서 훅 파일을 실행하고 echo $?가 1을 출력하는지 확인한 다음 해당 줄을 제거하십시오.

Bedrock 또는 Agent Platform으로 모델 요청 보내기

조직의 모델 요청이 자체 AWS 또는 Google Cloud 계정을 거쳐야 하는 경우, 러너를 Amazon Bedrock 또는 Google Cloud의 Agent Platform(이전 명칭 Vertex AI)용으로 구성합니다. 그러면 해당 러너가 시작하는 모든 세션은 사용자의 클라우드 자격 증명을 사용하여 사용자의 클라우드 계정에서 모델을 호출합니다. 이 구성이 없으면 세션은 모델 요청을 Anthropic API로 보냅니다. 러너는 여전히 세션을 받기 위해 Anthropic을 폴링하며, 각 세션은 여전히 이벤트 스트림을 api.anthropic.com으로 보냅니다. 이벤트 스트림에는 프롬프트, 응답, 도구 결과가 포함됩니다. 가용성 및 제한 사항의 플랜 요구 사항과 Zero Data Retention 제외 사항도 여전히 적용됩니다. 세션은 러너가 아니라 환경으로 라우팅되며, 다시 대기열에 추가되거나 재개된 세션은 다른 러너에서 실행될 수 있습니다. 환경의 모든 러너를 동일한 방식으로 구성하십시오. 시작하기 전에 이러한 제공업체에서 달라지는 점을 읽어 보십시오.
1

클라우드 계정과 이그레스 규칙 준비하기

모델 액세스, 범위가 좁게 지정된 정책 또는 역할, 네트워크 액세스를 설정합니다.
  • Amazon Bedrock: 사용 사례 세부 정보를 제출한 다음 IAM 구성에서 정책을 생성하고, bedrock:InvokeModel 및 bedrock:InvokeModelWithResponseStream을 세션이 사용하는 추론 프로필과 그 기반이 되는 파운데이션 모델로 제한합니다
  • Agent Platform: API를 활성화하고 모델 액세스를 요청한 다음, IAM 구성에 설명된 사용자 지정 역할을 aiplatform.endpoints.predict만 포함하여 생성합니다
  • 이그레스: 이그레스 규칙에서 제공업체의 엔드포인트를 허용합니다. 네트워크 요구 사항을 참조하십시오. 세션이 엔드포인트에 도달할 수 없으면 Claude Code가 세션에 오류가 표시되기 전까지 몇 시간 동안 재시도를 계속할 수 있습니다.
2

세션에 범위가 좁게 지정된 자격 증명 제공하기

1단계의 정책 또는 역할을 다른 작업은 전혀 할 수 없는 ID에 연결합니다. Claude Code가 허용하는 방법은 AWS 자격 증명 구성 및 GCP 자격 증명 구성을 참조하십시오.
프롬프트 인젝션을 포함하여 어떤 방식으로든 세션에서 코드를 실행할 수 있는 사람은 자격 증명이 유효한 동안 사용자의 비용으로 이 자격 증명을 사용할 수 있습니다. Claude Code는 세션 내부에서 실행되므로, 모델을 호출하는 데 사용하는 자격 증명은 세션 내에서 읽을 수 있어야 합니다.Claude가 실행하는 셸 명령, Claude Code 훅, stdio MCP 서버는 세션의 환경을 상속하며 Claude Code와 동일한 사용자로 실행됩니다. 따라서 이들은 자격 증명 변수와 자격 증명 파일을 읽을 수 있습니다.배포를 강화하면 호스트 자격 증명을 세션에서 차단할 수 있지만, 이 자격 증명은 차단할 수 없습니다. 이 자격 증명의 기반이 되는 ID에는 1단계의 정책 또는 역할 외에는 아무것도 부여하지 마십시오.
선택한 방법을 다음 러너 동작과 대조하여 확인하십시오.
  • 메타데이터 엔드포인트: 세션의 클라우드 메타데이터 엔드포인트 접근을 완전히 거부하면, 인스턴스 프로필처럼 이 엔드포인트에서 제공되는 자격 증명도 Claude Code에 도달하지 않습니다. Amazon EKS의 IAM Roles for Service Accounts(IRSA)나 Workload Identity Federation 자격 증명 파일 같은 파일 기반 웹 ID는 이 엔드포인트에 의존하지 않습니다.
  • 갱신: 세션이 자격 증명보다 오래 지속될 수 있으므로, 파일 기반 웹 ID처럼 스스로 갱신되는 방법을 사용하십시오
  • 래퍼 스크립트: 러너는 래퍼 스크립트를 세션당 한 번 시작하므로, 래퍼 스크립트가 내보내는 자격 증명은 갱신되지 않습니다. Claude Code는 환경에서 AWS 자격 증명을 읽으므로, 래퍼가 이미 다른 작업을 위해 AWS 자격 증명을 내보내고 있다면 Claude Code가 그 자격 증명으로 모델 요청에 서명할 수 있습니다.
3

러너 환경에 하나의 제공업체 변수 설정하기

컨테이너 사양이나 서비스 유닛 등 러너의 다른 환경 변수를 설정하는 위치에 정확히 하나의 제공업체 변수만 설정한 다음 러너를 다시 시작합니다. 예시에서는 셸 export로 표시합니다. 온디맨드 러너를 사용하는 경우, spawn-runner 훅이 시작하는 워크로드에 변수를 설정합니다.이러한 러너는 --confine-repo-settings enforce로 시작하십시오. 이 옵션은 커밋된 설정이 플래그된 저장소의 세션을 거부하므로, 먼저 기본 warn 모드로 실행하여 로그에 기록된 항목을 해결하십시오.
리전을 사용자의 리전으로 바꿉니다.
Claude Code가 리전을 결정하는 방식은 Claude Code 구성을 참조하십시오. Claude Code가 리전에 따라 사용하는 추론 프로필 접두사는 교차 리전 추론 프로필 접두사를 참조하십시오.
4

변수가 세션에 도달했는지 확인하기

호스트의 셸은 별개의 프로세스이므로 세션 내부에서 확인해야 합니다. 환경에서 세션을 시작하고 Claude에게 다음 명령을 실행하도록 요청합니다.
CLAUDE_CODE_USE_BEDROCK 또는 CLAUDE_CODE_USE_VERTEX를 1로 설정하는 줄이 표시되면 변수가 세션에 도달한 것입니다. 둘 다 표시되면 Claude Code는 Amazon Bedrock을 사용합니다. 출력이 없으면 어느 변수도 도달하지 않은 것입니다.이 명령은 트래픽이 아니라 구성을 보여 줍니다. 요청 자체를 확인하려면 클라우드 계정의 자체 지표나 요청 로그에서 찾아보십시오. 대신 첫 번째 메시지가 실패하면 Amazon Bedrock 또는 Agent Platform의 문제 해결을 참조하십시오.

Anthropic API 세션과 달라지는 점

Amazon Bedrock 또는 Google Cloud의 Agent Platform으로 모델 요청을 보내는 세션은 Anthropic API의 세션과 다음과 같은 점에서 다릅니다.
  • claude.ai의 정책: 서버 관리형 설정은 이러한 세션에 도달하지 않습니다. Owner가 Claude Code 관리자 설정에서 지정하는 조직 정책도 도달하지 않으므로, Claude Code는 세션 내에서 이를 적용하지 않습니다. 의존하는 규칙은 러너 이미지의 관리형 설정 파일에 넣으십시오.
  • 계정 스킬: 이러한 세션은 개인의 claude.ai 계정에서 활성화된 스킬을 다운로드하지 않습니다. 각 세션의 구성이 조합되는 방식을 참조하십시오.
  • 파일: claude.ai나 모바일 또는 데스크톱 앱에서 세션에 첨부한 파일은 세션에 도달하지 않으며, Claude는 SendUserFile 도구로 파일을 다시 보낼 수 없습니다. 대신 입력 파일을 저장소나 러너에 두십시오.
  • 모델 선택: Anthropic의 컨트롤 플레인이 각 세션의 모델을 전송하며, 모델 없이 세션이 시작되면 Claude Code는 해당 제공업체의 기본값을 사용합니다. 러너 환경의 ANTHROPIC_MODEL이나 ANTHROPIC_DEFAULT_MODEL로는 모델을 선택할 수 없지만, 별칭이 무엇으로 해석되는지는 고정할 수 있습니다.
    • ANTHROPIC_MODEL 및 ANTHROPIC_DEFAULT_MODEL: 제공업체 페이지의 예시에서는 ANTHROPIC_MODEL을 설정하지만, 러너는 세션에 전달하는 환경에서 이 두 변수를 제거합니다.
    • 모델 계열별 고정 변수: Amazon Bedrock 및 Agent Platform의 모델 버전 고정에 나오는 변수는 세션에 도달합니다. 이 변수는 opus 같은 별칭이 무엇으로 해석되는지를 결정하며, 전체 모델 ID가 무엇으로 해석되는지는 결정하지 않습니다.
  • 계정에서 제공하지 않는 모델: 세션이 모델 이름이 포함된 오류와 함께 메시지에서 실패할 수 있습니다. 개발자가 선택할 수 있는 모델, 모델 버전 고정에 설명된 백그라운드 모델, 자동 모드가 사용하는 분류기 모델을 활성화하십시오. Amazon Bedrock에서는 정책에서 각 모델을 허용하십시오.
  • 웹 검색 및 빠른 모드: 웹 검색은 Amazon Bedrock에서 사용할 수 없으며, 빠른 모드는 두 제공업체 모두에서 사용할 수 없습니다. 제공업체에 따라 달라지는 기타 기능은 제공업체별로 달라지는 CLI 기능을 참조하십시오.

MCP 서버

모든 세션에서 MCP 서버를 사용할 수 있게 하려면 데스크톱 설치에서 사용하는 것과 동일한 claude mcp add 명령으로 이미지 빌드 시점에 추가합니다. 러너가 컨테이너가 아닌 베어 프로세스인 경우, 호스트에서 러너의 사용자로 동일한 명령을 실행한 다음 러너를 다시 시작합니다. 러너는 시작 시 호스트 구성을 한 번만 읽습니다. --scope user 플래그는 필수입니다. 기본 로컬 범위는 디렉터리별 키 아래에 기록되며, 러너는 이 키를 세션에 시딩하지 않습니다. 예를 들어 Dockerfile에서는 다음과 같습니다:
러너는 시작 시 호스트의 구성을 한 번 스냅샷으로 저장합니다. 스냅샷은 호스트의 .claude.json에서 mcpServers 키를 캡처하며, 이 파일은 ~/.claude/ 내부가 아니라 그 옆에 위치합니다. 러너는 해당 키만 각 세션의 격리된 구성에 시딩하며, 계정 상태와 프로젝트 기록은 제외됩니다. 서버가 세션에 전달되었는지 확인하려면 해당 환경에서 세션을 시작하고 Claude에게 MCP 도구 목록을 요청합니다. 또한 러너는 캡처된 항목 중 type을 인식하지 못하는 항목에 대해 시작 경고를 로그에 기록하고 해당 항목을 제외하므로, 해당 서버가 세션에서 누락된 이유를 확인할 수 있습니다. SELF_HOSTED_RUNNER_HOST_CONFIG_DIR이 설정된 경우 러너는 대신 해당 디렉터리에서 .claude.json을 읽으므로, 이 변수를 빈 디렉터리로 지정하면 MCP 시딩도 비활성화됩니다. Claude Code는 다른 소스에서도 MCP 서버를 로드합니다:
  • 표준 시스템 경로에 있는 엔터프라이즈 범위 관리형 MCP 파일: Linux 러너 호스트에서는 /etc/claude-code/managed-mcp.json, macOS 호스트에서는 /Library/Application Support/ClaudeCode/managed-mcp.json입니다. 관리자가 나열한 서버만 로드할 수 있도록 잠긴 플릿에 사용합니다. 우선순위 규칙은 managed-mcp.json을 통한 배타적 제어를 참조하세요. 이 파일이 러너 호스트에 있으면 Claude Code는 claude.ai 커넥터를 포함하여 Anthropic의 컨트롤 플레인이 세션에 전달하는 MCP 서버를 건너뛰고, 세션 자식 프로세스의 stderr에 경고로 해당 서버 이름을 표시하며, 러너는 이를 debug 로그 수준으로 기록합니다. v2.1.229 이전에는 이러한 세션이 You cannot dynamically configure MCP servers when an enterprise MCP config is present와 함께 시작 시 종료되었습니다.
  • 러너 호스트의 관리형 설정에 있는 managedMcpServers 키: 배타적 제어를 하지 않고 HTTP 및 SSE 서버를 제공하므로 다른 소스의 서버도 계속 로드됩니다. Claude Code v2.1.259 이상이 필요합니다.
  • <repo>/.mcp.json: 프로젝트 범위입니다. 프로젝트 스레드 외부에서는 파일을 저장소에 커밋하면 해당 서버가 클라우드 세션에서 자동 승인됩니다. 여러 저장소가 있는 세션에서는 최대 하나의 저장소 파일만 로드됩니다.
  • 래퍼 스크립트에서 지정하는 --mcp-config 파일: exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --mcp-config /etc/claude/mcp.json과 같이 "$@" 뒤에 플래그를 덧붙입니다. 세션 도중 세션에 전달된 서버가 업데이트될 때 이 파일의 서버가 계속 실행되도록 하려면, exec처럼 래퍼가 전체 환경을 자식 프로세스에 전달하도록 합니다. v2.1.299 이전에는 이 파일의 stdio, HTTP, SSE 서버가 해당 서버를 명시하지 않은 첫 번째 업데이트 시점에 종료되었습니다.
조직에 커넥터 전달이 활성화되어 있으면 Anthropic의 컨트롤 플레인은 claude.ai에서 구성한 커넥터를 api.anthropic.com을 경유하는 서버 제공 MCP 구성을 통해 대화형으로 생성된 세션에 전달합니다. CLI 디스패치와 같이 프로그래밍 방식으로 생성된 세션은 커넥터 전달을 받지 않으므로, 이 섹션에 나열된 다른 소스를 통해 MCP 서버를 제공해야 합니다. 자식 프로세스의 OAuth 토큰에는 커넥터를 직접 가져오기 위한 범위가 포함되어 있지 않으므로 자식 프로세스는 직접 가져오기를 시도하지 않으며, 전달은 서버 주도로 이루어집니다.
커넥터를 전달받은 세션에서는 호스트의 .claude.json 또는 저장소의 .mcp.json에 있는 같은 이름의 서버가 연결되지 않습니다. 둘 다 사용하려면 서버에 다른 이름을 지정합니다.
settings.json에는 MCP 서버 정의가 포함되지 않으며, 설정 스키마에 최상위 mcpServers 필드도 없습니다. 관리형 설정에서는 대신 managedMcpServers 키로 서버를 제공합니다. 세션은 러너의 환경을 상속하므로, 러너에서 ENABLE_TOOL_SEARCH를 설정하면 러너가 생성하는 모든 세션의 MCP 도구 검색을 제어할 수 있습니다. 값에 대해서는 MCP 페이지에서 다룹니다.

첫 턴 전에 MCP 서버 대기하기

자체 호스팅 세션은 아직 연결 중인 MCP 서버를 두 개의 별도 시점에서 잠시 기다립니다. 대기 시간 내에 연결되지 못한 서버는 첫 턴이 시작될 때 해당 도구가 누락되며, 이후 별도의 조치 없이 사용할 수 있게 됩니다. 두 가지 대기는 다음과 같습니다:
  • 세션 시작: 도구 목록을 처음 가져오기 전에, 세션은 항목에 alwaysLoad: true를 설정한 HTTP 또는 SSE 서버를 기본적으로 최대 5초 동안 기다리며, 러너 환경에서 MCP_CONNECTION_NONBLOCKING=0을 설정한 경우에는 모든 서버를 기다립니다. 그 외의 경우 HTTP 및 SSE 서버는 백그라운드에서 연결됩니다. 세션이 여기서 대기하는 동안에는 초기화가 느려집니다. MCP_CONNECT_TIMEOUT_MS로 5초 기본값을 변경할 수 있습니다.
  • 첫 턴: 메시지가 도착한 후, 첫 턴은 아직 연결 중인 stdio 서버를 최대 2초 동안 기다립니다. 세션이 여기서 대기하는 동안에는 첫 응답이 느려집니다. 이 대기 시간을 변경하려면 러너 환경에서 CLAUDE_CODE_MCP_STARTUP_WAIT_MS를 설정합니다. 이 설정은 대기 대상 서버를 변경하지 않습니다. Claude Code v2.1.274 이상이 필요합니다.
claude mcp add에는 alwaysLoad 플래그가 없습니다. 이 키를 설정하려면 대신 claude mcp add-json으로 서버를 추가합니다. 이 명령은 서버의 JSON에서 해당 키를 받아 .claude.json에 기록합니다. Dockerfile에서는 다음과 같습니다:
이후 턴에서도 서버의 도구가 나타나지 않으면, MCP 서버에서 설명한 대로 서버가 세션에 전달되었는지 확인합니다.

기본 제공 세션 도구 끄기

Anthropic의 컨트롤 플레인은 Claude Code Remote라는 자체 MCP 서버를 클라우드 세션에 연결합니다. Claude는 이 서버의 도구를 사용하여 클라우드 예약 작업을 생성하고, 다른 클라우드 세션을 시작 및 조정하고, 추가 저장소를 연결하고, 풀 리퀘스트 활동을 추적합니다. 서버 전체를 끄려면 설정에 서버 수준 거부 규칙을 추가합니다. 컨트롤 플레인은 세션 생성 방식에 따라 세 가지 이름 중 하나로 서버를 등록합니다. Claude Code는 규칙의 이름을 대소문자를 포함하여 정확히 일치시키므로, 다음과 같이 이름마다 규칙을 한 번씩 작성합니다:
서버 전체를 지정하는 규칙은 서버에 나중에 추가되는 도구에도 적용됩니다. 하나의 도구만 끄고 나머지는 유지하려면 mcp__Claude_Code_Remote__add_repo와 같이 각 규칙에 밑줄 두 개와 도구 이름을 덧붙입니다. 도구를 제거하는 대신 서버 연결 자체를 차단하려면, 세 가지 이름을 mcp__ 접두사 없이 deniedMcpServers 아래의 serverName 항목으로 추가합니다. 러너를 변경하지 않고 세션에 적용하려면 서버 관리형 설정에 규칙을 넣고, 그렇지 않으면 러너의 ~/.claude/settings.json에 넣습니다. 모델 요청을 Bedrock 또는 Agent Platform으로 보내는 러너에서는 서버 관리형 설정이 해당 세션에 적용되지 않으므로 이 파일을 사용합니다. 러너의 설정이 세션에 적용되는 방식은 권한 및 도구 승인에서 설명합니다. 규칙이 적용되었는지 확인하려면 해당 환경에서 세션을 시작하고 Claude에게 MCP 도구 목록을 요청합니다. Claude Code는 거부된 도구를 Claude의 컨텍스트에서 제거하므로, 거부된 도구는 응답에 나타나지 않습니다.

세션에 작업 푸시 프롬프트

Anthropic 호스팅 세션은 Claude가 응답을 마칠 때 실행되는 Claude Code 훅인 Stop 훅을 실행하며, 작업을 커밋하고 푸시하도록 Claude에 프롬프트합니다. 러너는 하나를 설치하지 않습니다. 이 없이 커밋되지 않은 변경 사항으로 끝나는 세션은 해당 작업을 러너의 디스크에만 남기며 분기가 원격에 존재할 때까지 claude.ai/code의 Create PR 버튼은 비활성 상태로 유지됩니다. 아래의 참조 구현에는 두 부분이 있습니다. 설정 블록을 러너 호스트의 ~/.claude/settings.json에 병합합니다(러너가 모든 세션에 시드). 스크립트를 러너 호스트의 ~/.claude/hooks/stop-hook-nudge.sh로 저장하고 실행 가능하게 만듭니다:
훅은 세션이 끝나기 전에 Claude에게 커밋하고 푸시하도록 요청하며, 디렉토리가 git 저장소가 아니거나 원격이 없을 때는 아무것도 출력하지 않습니다. 저장소가 여러 개인 세션의 경우 $CLAUDE_PROJECT_DIR이 가리키는 대상을 참조하세요. Stop 훅은 Claude에 프롬프트할 뿐입니다. 커밋과 푸시가 작동하려면 다음을 설정합니다: Stop 훅은 턴당 최대 한 번만 프롬프트합니다. 커밋이나 푸시가 실패해도 턴은 종료됩니다. 세션에서 git의 오류를 찾아 해당 오류가 지적하는 문제를 해결하세요. 대규모 체크아웃에서는 Stop 훅 설정의 timeout을 늘리고 러너를 다시 시작합니다. 스크립트의 git status는 core.fsmonitor를 건너뛰므로, 해당 설정에 지정된 10초보다 오래 걸릴 수 있습니다. 시간 초과된 훅은 프롬프트 없이 턴을 종료합니다.

서브모듈과 Stop 훅

Stop 훅은 각 서브모듈 내부의 파일이 아니라 각 서브모듈이 어떤 커밋에 있는지를 확인합니다:
  • 서브모듈의 새 커밋: .gitmodules에서 해당 서브모듈에 ignore = all을 설정하더라도, 상위 저장소가 이를 기록할 때까지 Claude에 프롬프트합니다
  • 서브모듈의 커밋되지 않은 파일: Claude에 프롬프트하지 않으므로 해당 작업은 러너의 디스크에만 남습니다
  • 서브모듈의 푸시되지 않은 커밋: Stop 훅은 푸시 여부를 확인하지 않으므로, 상위 저장소가 다른 누구도 페치할 수 없는 커밋을 가리킬 수 있습니다

이전 버전의 Stop 훅 스크립트 교체

사용 중인 stop-hook-nudge.sh에서 GIT_OPTIONAL_LOCKS를 검색합니다. 없다면 이전 버전입니다. 설치한 모든 위치에서 현재 스크립트로 교체한 다음 러너를 다시 시작합니다. 대규모 체크아웃에서 timeout을 늘리는 경우를 제외하고 Stop 훅 설정은 그대로 유지합니다.
  • 시기: 세션이 Bash 샌드박스를 켠 상태로 실행된다면 지금 교체합니다. 그렇지 않다면 다음 이미지 빌드나 러너 재시작 시 교체합니다.
  • 변경 사항: 서브모듈의 커밋되지 않은 파일은 더 이상 Claude에 프롬프트하지 않습니다. .gitmodules에서 ignore = all로 표시된 서브모듈의 새 커밋은 이제 프롬프트합니다.
훅은 Bash 샌드박스 외부에서 실행되므로, 현재 스크립트의 git 호출은 체크아웃의 git 설정에 지정된 프로그램을 시작할 수 있는 일부 git 기능을 건너뜁니다. 모두 건너뛰지는 않습니다. 샌드박스에만 의존하지 말고, 배포 강화에 설명된 대로 세션을 격리하세요.

권한 및 도구 승인

자체 호스팅 세션에는 연결된 터미널이 없으므로, 응답하지 않은 권한 프롬프트는 사용자가 UI에서 응답할 때까지 턴을 멈춥니다. Anthropic의 컨트롤 플레인은 각 세션의 도구 목록과 권한 규칙을 작업 페이로드와 함께 전송합니다. 기본 구성은 Bash를 포함한 일상적인 도구 호출을 사전 승인하며, 클라우드 세션은 모드와 관계없이 파일 편집을 사전 승인합니다. 사전 승인되지 않은 호출은 세션 UI를 통해 확인을 요청합니다.
자동 모드는 세션 컨테이너가 기본 거부 네트워크 이그레스와 나머지 보안 강화 섹션의 조치가 적용된 상태로 실행되는 환경에서만 고정하십시오. 기본 사전 승인 도구 세트와 자동 모드 모두에서 Bash 네트워크 요청을 포함한 일상적인 도구 호출은 사람의 개입 없이 실행되므로, 이러한 호출이 도달할 수 있는 범위를 제한하는 것은 네트워크 경계입니다.
컨트롤 플레인이 무엇을 전송하든 프롬프트를 최소화하려면 래퍼 스크립트 또는 command 훅에서 자동 모드를 고정하십시오. 자동 모드를 사용하면 세션이 일상적인 권한 프롬프트 없이 실행됩니다. 별도의 분류기 모델이 작업을 실행 전에 검토하고 거부한 작업을 차단하며, 명시적인 확인 규칙은 여전히 프롬프트를 강제합니다. 분류기가 확인하는 항목은 권한 모드 페이지에서 다룹니다. 러너는 래퍼를 호출하기 전에 서버에서 계산한 플래그를 덧붙이며, --permission-mode와 같은 단일 값 플래그의 경우 파서가 마지막으로 나타난 값을 따르므로 "$@" 뒤에 덧붙인 플래그가 서버에서 전송한 값을 재정의합니다.
대신 특정 도구를 사전 승인하려면 --allowed-tools "Bash(bazel *) Bash(yarn *) mcp__internal__*"와 같이 규칙과 함께 --allowed-tools를 덧붙이십시오. --allowed-tools 및 --disallowed-tools와 같은 목록 플래그는 재정의되지 않고 여러 번 나타날 때마다 누적되므로, 사용자의 규칙은 컨트롤 플레인이 전송하는 규칙 위에 적용됩니다. 범위를 좁히려면 --disallowed-tools를 덧붙이십시오. 이 플래그는 다른 규칙이 허용하더라도 도구를 거부합니다.

각 세션의 구성이 조합되는 방식

러너는 각 세션에 고유한 설정 디렉터리를 제공하며, 이 디렉터리는 러너가 시작 시 한 번 캡처하는 호스트의 ~/.claude/ 스냅샷으로 초기화됩니다. 러너 이미지에 있는 settings.json, CLAUDE.md, 훅, 에이전트, 명령 및 스킬은 사용자 수준 기준선으로서 모든 세션에 적용됩니다. 실행 중인 호스트에서 구성을 변경하면 러너를 다시 시작한 후에만 변경 사항이 적용됩니다. 다른 경로에서 초기화하려면 SELF_HOSTED_RUNNER_HOST_CONFIG_DIR을 설정하고, 초기화를 비활성화하려면 빈 디렉터리를 지정하십시오. 세션은 다음 설정 파일도 읽습니다. 이러한 소스가 적용되는 순서는 설정 우선순위를 참조하십시오. Anthropic의 컨트롤 플레인이 세션에 Claude Code 훅을 제공하면, 러너는 이를 사용자의 구성을 덮어쓰지 않고 함께 설치합니다. Claude Code v2.1.229 이상이 필요하며, 이전 버전의 러너는 제공된 훅을 무시합니다. 제공된 훅은 python3를 실행하므로 러너 이미지에 python3를 설치하십시오.
  • 설치 위치: 러너는 제공된 각 훅 스크립트를 세션 설정 디렉터리의 예약된 hooks/.ccr-launcher/ 하위 디렉터리에 작성하고, --settings로 세션에 전달하는 별도의 설정 파일에 스크립트를 등록합니다. 이때 초기화된 settings.json과 hooks/<name>에 있는 사용자 자신의 스크립트는 변경되지 않습니다. 러너는 세션마다 예약된 하위 디렉터리를 다시 생성하며, ~/.claude/hooks/.ccr-launcher/에 있는 호스트 콘텐츠를 세션으로 초기화하지 않습니다.
  • 작성 주체: 컨트롤 플레인은 세션별 입력이나 서드 파티 입력이 아닌, 자체 배포의 고정된 상수로 스크립트를 채웁니다.
  • 여전히 적용되는 제어: --settings를 통해 전달된 훅은 관리형 계층이 아닌 일반적인 병합 훅 구성에 포함되므로, 관리형 설정이 여전히 적용됩니다. disableAllHooks는 이 훅을 비활성화하며, 이 훅은 allowManagedHooksOnly가 계속 로드하는 범주에 속하지 않습니다.
사람이 직접 세션을 시작하면 Claude Code는 해당 claude.ai 계정에서 활성화된 스킬을 해당 세션의 설정 디렉터리에 다운로드합니다. 클라우드 예약 작업 실행은 소유자의 스킬을 받지 않으며, Bedrock 또는 Agent Platform으로 모델 요청을 보내는 세션은 스킬을 전혀 다운로드하지 않습니다. 이러한 세션에 필요한 스킬은 러너 이미지에 추가하거나, 프로젝트 스레드 외부에서는 저장소의 .claude/skills/에 커밋하십시오. Claude Tag 세션을 제외하고, 자체 호스팅 환경의 세션은 기본적으로 자동 메모리가 꺼진 상태로 실행됩니다. 세션 간에 유지되어야 하는 지침은 러너 이미지나 저장소의 CLAUDE.md를 사용하십시오. 러너의 호스트 ~/.claude/ 스냅샷에는 projects/ 디렉터리가 포함되지 않습니다. 자동 메모리의 기본 스토리지 위치는 이 디렉터리 아래에 있습니다. 이곳에 메모리 파일을 두더라도 러너는 이를 세션으로 초기화하지 않으며, 해당 파일이 자동 메모리를 켜지도 않습니다.

서버 관리형 설정을 가져오지 못한 세션 중지

서버 관리형 설정을 가져오는 세션이 가져오기에 실패했을 때 시작되지 않도록 하려면 러너 이미지의 managed-settings.json에서 forceRemoteSettingsRefresh를 true로 설정하십시오. 조직에 서버 관리형 설정이 없다는 Anthropic API의 응답은 가져오기 성공으로 간주됩니다. 키를 추가하기 전에 해당 환경이 Claude Tag 채널 세션을 실행하는지, 또는 Bedrock이나 Agent Platform으로 모델 요청을 보내는지 확인하십시오.
  • Claude Tag 채널 세션: Owner가 Claude Tag 채널을 라우팅하는 환경의 러너에서는 이 키를 넣지 마십시오. Anthropic API는 이러한 세션의 서버 관리형 설정 요청을 거부하므로, 키가 설정되어 있으면 세션이 시작되지 않습니다. 이러한 세션의 규칙은 러너 이미지의 managed-settings.json에 넣으십시오. 이 세션은 해당 파일을 여전히 읽습니다. 다른 세션에 이 키를 사용하려면 해당 세션을 별도의 환경에서 실행하십시오.
  • Bedrock 또는 Agent Platform: 이러한 제공자의 세션은 서버 관리형 설정을 가져오지 않으므로, 키 설정 여부와 관계없이 시작됩니다. 대신 동일한 managed-settings.json에서 규칙을 적용하십시오.
다음 managed-settings.json은 이 키를 설정합니다.
managed-settings.json
claude.ai 관리자 콘솔에서도 키를 설정하더라도 이 파일에 키를 설정하십시오. 새 세션에는 서버 관리형 설정의 캐시된 사본이 없으므로, 콘솔에서만 키를 설정하면 세션에는 아직 해당 키가 없습니다. 이 상태에서 가져오기가 실패하면 세션은 이후 가져오기가 성공할 때까지 해당 설정 없이 실행됩니다. 세션이 시작되지 않은 원인이 설정 가져오기 실패인지 확인하려면 세션에서 Claude Code의 마지막 출력이나 러너의 로그를 확인하십시오. 출력에는 Your organization requires remote managed settings to load, but they could not be loaded.가 포함됩니다.

여러 저장소가 있는 세션의 저장소 설정

프로젝트의 스레드에서는 스레드가 어디에서 시작되든 어떤 저장소의 .claude/settings.json도 프로젝트 설정으로 적용되지 않으며, 어떤 저장소의 .mcp.json이나 스킬도 로드되지 않습니다. 이러한 스레드에 스킬을 제공하려면 러너 이미지에 추가하십시오. 여러 저장소가 있는 그 밖의 세션에서 Claude Code는 세션이 시작되는 디렉터리에서 프로젝트 설정을 읽으므로, 최대 하나의 저장소의 .claude/settings.json만 프로젝트 설정으로 적용됩니다. 다른 저장소의 파일에 정의된 훅은 실행되지 않고, 그 안의 거부 규칙은 적용되지 않으며, 해당 env도 설정되지 않습니다.
  • 기본값인 --capacity 1과 기본 제공 체크아웃 사용 시: 세션은 저장소 목록의 첫 번째 저장소에서 시작됩니다. 해당 저장소의 .claude/settings.json이 프로젝트 설정으로 적용되고 .mcp.json이 로드되며, 다른 저장소의 파일은 적용되거나 로드되지 않습니다.
  • 1보다 큰 --capacity 또는 checkout 훅 사용 시: 세션은 체크아웃이 포함된 세션별 디렉터리에서 시작됩니다. 어떤 저장소의 .claude/settings.json도 프로젝트 설정으로 적용되지 않고, 어떤 저장소의 .mcp.json도 로드되지 않으며, 훅 명령의 $CLAUDE_PROJECT_DIR은 체크아웃이 아닌 해당 디렉터리가 됩니다.
각 저장소의 CLAUDE.md는 세션이 어디에서 시작되든 로드됩니다. 프로젝트 스레드 외부에서는 해당 저장소의 스킬도 로드됩니다. 러너는 모든 저장소를 추가 디렉터리로 Claude Code에 전달하므로, Claude Code는 각 저장소의 .claude/settings.json에서 enabledPlugins 및 extraKnownMarketplaces 키도 읽습니다. 모든 세션에서 훅을 실행하거나 권한 규칙을 적용하려면 러너 호스트의 ~/.claude/settings.json에 넣으십시오. 러너는 세션이 어디에서 시작되든 호스트 파일을 모든 세션으로 초기화합니다. Read 또는 Edit 규칙의 경로는 // 절대 경로 또는 ~/ 홈 기준 상대 경로 패턴으로 작성하십시오. 다른 패턴은 설정 소스나 현재 디렉터리를 기준으로 고정되기 때문입니다.

저장소에 커밋된 권한 규칙

저장소에 커밋된 permissions.allow에 경로 없는 "Edit", "Write" 또는 "NotebookEdit" 항목을 넣지 마십시오. 경로 없는 파일 도구 규칙은 경로와 관계없이 도구와 일치하여 워크스페이스뿐만 아니라 호스트의 모든 위치에 쓰기를 허용하므로, 러너의 쓰기 범위 제한 가드가 해당 세션을 표시합니다. --confine-repo-settings enforce를 사용하면 로그를 기록하고 계속하는 대신 세션 생성을 거부합니다. 보안 강화 섹션을 참조하십시오. 저장소에는 파일 도구 규칙이 전혀 필요하지 않습니다. 클라우드 세션은 모드와 관계없이 파일 편집을 사전 승인합니다. 규칙을 커밋하는 경우 "Edit(/**)"와 같이 워크스페이스로 범위를 지정하십시오. 선행 슬래시 하나는 프로젝트 루트, 즉 세션의 워크스페이스를 기준으로 하는 상대 경로입니다. 운영자의 호스트 수준 settings.json은 저장소에 커밋되는 파일이 아니므로, 이 파일에서는 경로 없는 파일 도구 규칙을 사용해도 괜찮습니다. auto의 defaultMode는 이미지 전체 또는 사용자 수준 설정 파일에서만 적용되므로, 체크아웃된 저장소는 스스로 자동 모드를 부여할 수 없습니다. 클라우드 세션이 허용하는 모드와 전체 규칙 구문은 권한 모드를 참조하십시오.

다음 단계

  • 참조: 모든 CLI 플래그, 환경 변수 및 메트릭
  • 세션 ID 확인: 러너 외부 서비스에서 세션 토큰 검증